将 Func 委托与 Async 方法一起使用

Posted

技术标签:

【中文标题】将 Func 委托与 Async 方法一起使用【英文标题】:Using Func delegate with Async method 【发布时间】:2016-09-13 19:21:13 【问题描述】:

我正在尝试将 Func 与异步方法一起使用。我遇到了一个错误。

无法将异步 lambda 表达式转换为委托类型 'Func<HttpResponseMesage>'。异步 lambda 表达式可能返回 void、Task 或 Task<T>,它们都不能转换为 'Func<HttpResponseMesage>'

下面是我的代码:

public async Task<HttpResponseMessage> CallAsyncMethod()

    Console.WriteLine("Calling Youtube");
    HttpClient client = new HttpClient();
    var response = await client.GetAsync("https://www.youtube.com/watch?v=_OBlgSz8sSM");
    Console.WriteLine("Got Response from youtube");
    return response;


static void Main(string[] args)

    Program p = new Program();
    Task<HttpResponseMessage> myTask = p.CallAsyncMethod();
    Func<HttpResponseMessage> myFun =async () => await myTask;
    Console.ReadLine();

【问题讨论】:

我在async delegate types 上有一篇博文,您可能会觉得有帮助。 代码好像有bug:Error CS4010 Cannot convert async lambda expression to delegate type 'Func&lt;HttpResponseMessage&gt;'. An async lambda expression may return void, Task or Task&lt;T&gt;, none of which are convertible to 'Func&lt;HttpResponseMessage&gt;'.正确的一定是Func&lt;Task&lt;HttpResponseMessage&gt;&gt; myFun =async () =&gt; await myTask; 【参考方案1】:

正如错误所说,异步方法返回TaskTask&lt;T&gt;void。因此,要使其正常工作,您可以:

Func<Task<HttpResponseMessage>> myFun = async () => await myTask;

【讨论】:

请注意,异步操作可能在用户按下键时尚未完成,Console.ReadLine() 已完成。除非您在Task 上明确Wait,否则应用程序可能会在异步操作完成之前终止。【参考方案2】:

我通常采取的路径是让Main 方法调用一个返回Task 的Run() 方法,并在Task 上调用.Wait() 来完成。

class Program

    public static async Task<HttpResponseMessage> CallAsyncMethod()
    
        Console.WriteLine("Calling Youtube");
        HttpClient client = new HttpClient();
        var response = await client.GetAsync("https://www.youtube.com/watch?v=_OBlgSz8sSM");
        Console.WriteLine("Got Response from youtube");
        return response;
    

    private static async Task Run()
    
        HttpResponseMessage response = await CallAsyncMethod();
        Console.ReadLine();
    

    static void Main(string[] args)
    
        Run().Wait();
    

这允许您的控制台应用程序的其余部分在完全支持异步/等待的情况下运行。由于控制台应用程序中没有任何 UI 线程,因此使用 .Wait() 不会冒死锁的风险。

【讨论】:

这将使用 Wait() 阻塞任务,并且不再是执行异步上下文的正确方法。请阅读另一个问题here 您提供的链接是针对 MVC 应用程序的,在这里您是正确的。在 C# 7.2 之前,这是您必须在控制台应用程序中执行的操作,否则控制台应用程序会在异步操作完成之前完成执行并关闭。在控制台应用程序中无需担心任何 SyncContext。但是,C# 7.2 允许您的 Main 方法返回一个异步任务,以便您可以在控制台应用程序中等待。【参考方案3】:

代码修复如:

static void Main(string[] args)
        
            Program p = new Program();
            Task<HttpResponseMessage> myTask = p.CallAsyncMethod();
            Func<Task<HttpResponseMessage>> myFun = async () => await myTask;
            Console.ReadLine();
        

【讨论】:

【参考方案4】:

在 Func 内部运行任务,等待它并检查异常,然后返回结果。

Func<HttpResponseMessage> myFun = () => 

   var t = Task.Run(async () => await myTask);
   t.Wait();
   if (t.IsFaulted)
      throw t.Exception;
   return t.Result;
;

【讨论】:

为什么会被否决?我正在尝试学习异步编程,这真的会帮助我了解这一点。 @Eric - 因为它使用的 Task.Run() 机制在技术上不是“异步”的。它更多的是并行/并发构造。关于异步与并行的普遍共识是:异步 -> I/O 操作(考虑数据库、磁盘等访问)并行 -> CPU 密集型任务,例如在大型 int 上计算斐波那契计算 非常有帮助。我正在做高性能计算,但一直找不到好的模式,可能是因为我一直在寻找异步而不是并行计算。

以上是关于将 Func 委托与 Async 方法一起使用的主要内容,如果未能解决你的问题,请参考以下文章

Func与Action

将 Async/Await 与 Pickle 一起使用

将 async-await 与 node-fetch 一起使用不会将响应返回给调用方法

C#中Func与Action的理解

如何将 RestSharp 与 async/await 一起使用

async 与 await 线程调用顺序