如何使用方法异步 Task 并返回字符串,然后如何将该方法的委托传递给构造函数并在以后使用它

本文关键字:构造函数 string Task 异步 使用方法 返回 然后 字符串 方法 | 更新日期: 2023-09-27 18:20:01

我有这个返回string的方法:

public string SendResponse(HttpListenerRequest request)
{
    string result = "";
    string key = request.QueryString.GetKey(0);
    if (key == "cmd")
    {
        if (request.QueryString[0] == "uploadstatus")
        {
            switch (Youtube_Uploader.uploadstatus)
            {
                case "uploading file":
                    return "uploading " + Youtube_Uploader.fileuploadpercentages;
                case "status":
                    return Youtube_Uploader.fileuploadpercentages.ToString();
                case "file uploaded successfully":
                    Youtube_Uploader.uploadstatus = "";
                    return "upload completed," + Youtube_Uploader.fileuploadpercentages + ","
                       + Youtube_Uploader.time;
                default:
                    return "upload unknown state";
            }

        }
        if (request.QueryString[0] == "nothing")
        {
            return "Connection Success";
        }
        if (request.QueryString[0] == "start")
        {
            StartRecrod();
            result = "Recording started";
        }
        if (request.QueryString[0] == "stop")
        {
            dirchanged = false;
            StartRecrod();
            result = "Recording stopped and preparing the file to be shared on youtube";
            string fileforupload = await WatchDirectory();
            await WaitForUnlockedFile(fileforupload);
            using (StreamWriter w = new StreamWriter(userVideosDirectory + "''UploadedVideoFiles.txt", true))
            {
                w.WriteLine(fileforupload);
            }
            uploadedFilesList.Add(fileforupload);
            Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
        }
    }
    else
    {
        result = "Nothing have been done";
    }
    return result;
}

问题是在这种方法中,我在两行中使用await

string fileforupload = await WatchDirectory();
await WaitForUnlockedFile(fileforupload);

并在这两行上出现错误。这两个错误是相同的:

错误 "await"运算符只能在异步方法中使用。 考虑使用"async"修饰符标记此方法并更改 其返回类型为 'Task<string>'

这个问题是否可以使SendResponse()方法返回字符串,因为我需要它并使用await

这是两种方法,这就是为什么我需要在SendResponse()方法中使用await

private async Task<string> WatchDirectory()
{
    using (FileSystemWatcher watcher = new FileSystemWatcher())
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        watcher.Path = userVideosDirectory;
        watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
        watcher.Filter = "*.mp4";
        watcher.Changed += (sender, e) => tcs.SetResult(e.FullPath);
        watcher.EnableRaisingEvents = true;
        return await tcs.Task;
    }
}
// You can get rid of the OnChanged() method altogether
private async Task WaitForUnlockedFile(string fileName)
{
    while (true)
    {
        try
        {
            using (IDisposable stream = File.Open(fileName, FileMode.OpenOrCreate,
                FileAccess.ReadWrite, FileShare.None))
            { /* on success, immediately dispose object */ }
            break;
        }
        catch (IOException)
        {
            // ignore exception
            // NOTE: for best results, consider checking the hresult value of
            // the exception, to ensure that you are only ignoring the access violation
            // exception you're expecting, rather than other exceptions, like
            // FileNotFoundException, etc. which could result in a hung process
        }
        // You might want to consider a longer delay...maybe on the order of
        // a second or two at least.
        await Task.Delay(100);
    }
}

更新:

我将方法SendResponse()更改为async Task<string>但是在 WebServer 类构造函数中,我有这个并在这一行出现错误:

WebServer ws = new WebServer(SendResponseAsync, "http://+:8098/");

(SendResponseAsync是SendResponse改名的(

错误是:

错误 1"System.Threading.Tasks.Task Automatic_Record.Form1.SendResponseAync(System.Net.HttpListenerRequest("具有错误的返回类型

而网络服务器类是:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Threading;
namespace Automatic_Record
{
    class WebServer
    {
        private readonly HttpListener _listener = new HttpListener();
        private readonly Func<HttpListenerRequest, string> _responderMethod;
        public WebServer(string[] prefixes, Func<HttpListenerRequest, string> method)
        {
            if (!HttpListener.IsSupported)
                throw new NotSupportedException(
                    "Needs Windows XP SP2, Server 2003 or later.");
            // URI prefixes are required, for example 
            // "http://localhost:8080/index/".
            if (prefixes == null || prefixes.Length == 0)
                throw new ArgumentException("prefixes");
            // A responder method is required
            if (method == null)
                throw new ArgumentException("method");
            foreach (string s in prefixes)
                _listener.Prefixes.Add(s);
            _responderMethod = method;
            _listener.Start();
        }
        public WebServer(Func<HttpListenerRequest, string> method, params string[] prefixes)
            : this(prefixes, method) { }
        public void Run()
        {
            ThreadPool.QueueUserWorkItem((o) =>
            {
                Console.WriteLine("Webserver running...");
                try
                {
                    while (_listener.IsListening)
                    {
                        ThreadPool.QueueUserWorkItem((c) =>
                        {
                            var ctx = c as HttpListenerContext;
                            try
                            {
                                string rstr = _responderMethod(ctx.Request);
                                System.Diagnostics.Trace.Write(ctx.Request.QueryString);
                                //ctx.Request.QueryString
                                byte[] buf = Encoding.UTF8.GetBytes(rstr);
                                ctx.Response.ContentLength64 = buf.Length;
                                ctx.Response.OutputStream.Write(buf, 0, buf.Length);
                                System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
                            }
                            catch { } // suppress any exceptions
                            finally
                            {
                                // always close the stream
                                ctx.Response.OutputStream.Close();
                            }
                        }, _listener.GetContext());
                    }
                }
                catch { } // suppress any exceptions
            });
        }
        public void Stop()
        {
            _listener.Stop();
            _listener.Close();
        }
    }
}

更新 2

我尝试了彼得解决方案,所以我将 WebServer 类代码更改为彼得在此问题解决方案中显示的代码。

然后在 form1 构造函数中,我做到了:

var ws = new WebServer(
            () => Task.Run(request => SendResponseAsync(request)),
            "http://+:8098/");
            ws.Run();

然后使用方法发送响应异步:

public async Task<string> SendResponseAsync(HttpListenerRequest request)
        {
            string result = "";
            string key = request.QueryString.GetKey(0);
            if (key == "cmd")
            {
                if (request.QueryString[0] == "uploadstatus")
                {
                    switch (Youtube_Uploader.uploadstatus)
                    {
                        case "uploading file":
                            return "uploading " + Youtube_Uploader.fileuploadpercentages;
                        case "status":
                            return Youtube_Uploader.fileuploadpercentages.ToString();
                        case "file uploaded successfully":
                            Youtube_Uploader.uploadstatus = "";
                            return "upload completed," + Youtube_Uploader.fileuploadpercentages + ","
                               + Youtube_Uploader.time;
                        default:
                            return "upload unknown state";
                    }

                }
                if (request.QueryString[0] == "nothing")
                {
                    return "Connection Success";
                }
                if (request.QueryString[0] == "start")
                {
                    StartRecrod();
                    result = "Recording started";
                }

                if (request.QueryString[0] == "stop")
                {
                    dirchanged = false;
                    StartRecrod();
                    result = "Recording stopped and preparing the file to be shared on youtube";
                    string fileforupload = await WatchDirectory();
                    await WaitForUnlockedFile(fileforupload);
                    using (StreamWriter w = new StreamWriter(userVideosDirectory + "''UploadedVideoFiles.txt", true))
                    {
                        w.WriteLine(fileforupload);
                    }
                    uploadedFilesList.Add(fileforupload);
                    Youtube_Uploader youtubeupload = new Youtube_Uploader(uploadedFilesList[0]);
                }

            }
            else
            {
                result = "Nothing have been done";
            }
            return result;
        }

守望目录:

private async Task<string> WatchDirectory()
        {
            using (FileSystemWatcher watcher = new FileSystemWatcher())
            {
                TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
                watcher.Path = userVideosDirectory;
                watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
                watcher.Filter = "*.mp4";
                watcher.Changed += (sender, e) => tcs.SetResult(e.FullPath);
                watcher.EnableRaisingEvents = true;
                return await tcs.Task;
            }
        }

最后等待解锁文件

private async Task WaitForUnlockedFile(string fileName)
        {
            while (true)
            {
                try
                {
                    using (IDisposable stream = File.Open(fileName, FileMode.OpenOrCreate,
                        FileAccess.ReadWrite, FileShare.None))
                    { /* on success, immediately dispose object */ }
                    break;
                }
                catch (IOException)
                {
                    // ignore exception
                    // NOTE: for best results, consider checking the hresult value of
                    // the exception, to ensure that you are only ignoring the access violation
                    // exception you're expecting, rather than other exceptions, like
                    // FileNotFoundException, etc. which could result in a hung process
                }
                // You might want to consider a longer delay...maybe on the order of
                // a second or two at least.
                await Task.Delay(100);
            }
    }

但是在线上出现错误:

任务运行严重性代码描述项目文件行错误 无法将 lambda 表达式转换为类型"string[]",因为它不是委托类型Automatic_Record

还有"http://+:8098/"行上的错误严重性代码描述项目文件行错误参数 2:无法从"字符串"转换为"System.Func>

如何使用方法异步 Task<string> 并返回字符串,然后如何将该方法的委托传递给构造函数并在以后使用它

是否可以使发送响应返回的问题 字符串,因为我需要它并且还要使用 await ?

异步是"一路走来"。这意味着一旦您开始在代码中使用await,您的方法签名就会向上传播,通过整个调用堆栈。这意味着代码中的任何async方法都必须返回TaskTask<T>,并向其添加async修饰符,以便编译器检测到这是一个异步方法,需要转换为状态机。

这意味着此同步签名:

public string SendResponse(HttpListenerRequest request)

需要转换为异步签名:

public async Task<string> SendResponseAync(HttpListenerRequest request)

有一个选项可以使用 Task.Result 同步阻止您的代码。我不推荐它,因为您不应该阻止异步代码。这只会导致麻烦(通常是死锁(。

正如回答者 Yuval 所说(正如我对你上一个问题的回答所说(,一旦你从 async 开始,它通常必须一直传播到调用堆栈。也就是说,还有其他选择:

  • 正如我在之前的回答中提到的,您可以同步等待异步操作。不理想,实际上只是一个权宜之计,直到您可以进一步改进代码,但它可以工作。
  • 您可以捕获异步操作(即Task(并在以后使用它。

在您的特定示例中,第二个选项应该可以正常工作。 也就是说,您需要修复的第一件事就是简单地调整构造函数,以便它可以接受异步方法。然后,您可以稍后调用该方法,最好是异步调用。

例如:

class WebServer
{
    private readonly HttpListener _listener = new HttpListener();
    private readonly Func<HttpListenerRequest, Task<string>> _responderMethod;
    public WebServer(string[] prefixes, Func<HttpListenerRequest, Task<string>> method)
    {
        if (!HttpListener.IsSupported)
            throw new NotSupportedException(
                "Needs Windows XP SP2, Server 2003 or later.");
        // URI prefixes are required, for example 
        // "http://localhost:8080/index/".
        if (prefixes == null || prefixes.Length == 0)
            throw new ArgumentException("prefixes");
        // A responder method is required
        if (method == null)
            throw new ArgumentException("method");
        foreach (string s in prefixes)
            _listener.Prefixes.Add(s);
        _responderMethod = method;
        _listener.Start();
    }
    public WebServer(Func<HttpListenerRequest, Task<string>> method, params string[] prefixes)
        : this(prefixes, method) { }
    public void Run()
    {
        ThreadPool.QueueUserWorkItem((o) =>
        {
            Console.WriteLine("Webserver running...");
            try
            {
                while (_listener.IsListening)
                {
                    ThreadPool.QueueUserWorkItem(async (c) =>
                    {
                        var ctx = c as HttpListenerContext;
                        try
                        {
                            string rstr = await _responderMethod(ctx.Request);
                            System.Diagnostics.Trace.Write(ctx.Request.QueryString);
                            //ctx.Request.QueryString
                            byte[] buf = Encoding.UTF8.GetBytes(rstr);
                            ctx.Response.ContentLength64 = buf.Length;
                            ctx.Response.OutputStream.Write(buf, 0, buf.Length);
                            System.Data.SqlClient.SqlConnectionStringBuilder builder = new System.Data.SqlClient.SqlConnectionStringBuilder();
                        }
                        catch { } // suppress any exceptions
                        finally
                        {
                            // always close the stream
                            ctx.Response.OutputStream.Close();
                        }
                    }, _listener.GetContext());
                }
            }
            catch { } // suppress any exceptions
        });
    }
    public void Stop()
    {
        _listener.Stop();
        _listener.Close();
    }
}


当然,随着更改的构造函数,任何其他调用方(如果存在(也必须更改。理想情况下,您将更改与这些调用方相关的代码,以便它也遵循async模型,充分利用并获得该方法的全部好处。

但同样,如果您不能或不会这样做,则可以使较旧的同步代码适应异步模型。 例如,如果您有这样的东西:

var server = new WebServer(SomeOtherSendResponse, "http://+:8098/");

...你可以把它改成这样:

var server = new WebServer(
    request => Task.Run(() => SomeOtherSendResponse(request)),
    "http://+:8098/");

或者,也可以创建一个构造函数重载来包装任何此类调用方的同步方法,以便原始调用站点可以保持不变:

public WebServer(Func<HttpListenerRequest, string> method, params string[] prefixes)
    : this(request => Task.Run(() => method(request)), prefixes)
{ }

请注意,通过一直沿async调用堆栈向上,代码中操作必须长时间等待某些异步事件或操作的位置都不会导致代码阻塞。相反,它们只是将控制权返回给执行线程,允许框架稍后在异步事件或操作发生或完成时继续执行 async 方法。

如果您尝试通过简单地同步等待异步操作完成来"解决"问题,您将(在这种特殊情况下(绑定线程池线程,阻止它,直到它可以继续。这样,线程池线程将在异步操作启动后立即返回到池中,并且程序不需要再次使用该线程池线程进行该操作,直到操作本身实际完成。


顺便说一句:作为一般规则,不建议使用忽略几乎所有可能发生的异常的代码。您应该只捕获那些您期望发生的异常,并且您肯定知道可以安全地忽略这些异常。即使在那里,您至少应该以某种方式报告它们,以帮助您或用户诊断代码中不存在的问题。其他异常很容易使程序的执行处于损坏状态。充其量,这些问题将非常难以诊断和修复,最坏的情况是,您最终可能会损坏用户状态,输出数据等。

SendResponse声明为Task<string>。这表示此任务将返回一个字符串。

public async Task<string> SendResponse(HttpListenerRequest request)
{
    ...
}

在你调用它的时候:

string result = await SendRespone(request);
我相信

您最新的编译错误是由于没有向 Web 服务器类提供具有正确签名的 lambda。因此,您需要更改传递构造函数的内容或它所期望的内容。以您实现 Web 服务器的方式,它需要一个非异步方法来_responderMethod。因此,试图使 SendResponse(( 异步是适得其反的。我强烈建议重构Web服务器以异步处理响应,或者回到使SendResponse((非异步。

如果你想让你的代码按原样编译,你可以尝试这样的事情:

var ws = new WebServer(
        request =>
           {
              var t = SendResponseAsync(request);
              t.Wait();
              return t.Result;
           },
        "http://+:8098/");
        ws.Run();

您可能还必须将 lambda 包装在正确的委托类型中,如下所示:

var ws = new WebServer(
        new Func<HttpListenerRequest, string>(request =>
           {
              var t = SendResponseAsync(request);
              t.Wait();
              return t.Result;
           }),
        "http://+:8098/");
        ws.Run();