使用Func委托与异步方法

106

我正在尝试将Async方法与Func一起使用,但是出现了错误。

无法将Async Lambda表达式转换为委托类型'Func<HttpResponseMesage>'。 Async 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();
}

7
我有一篇关于异步委托类型的博客文章,您可能会觉得很有帮助。 - Stephen Cleary
代码似乎有一个错误:Error CS4010 无法将异步 lambda 表达式转换为委托类型 'Func<HttpResponseMessage>'。异步 lambda 表达式可能返回 void、Task 或 Task<T>,这些都不能转换为 'Func<HttpResponseMessage>'。 正确的应该是 Func<Task<HttpResponseMessage>> myFun =async () => await myTask; - Code Pope
4个回答

148

正如错误提示所说,异步方法返回TaskTask<T>void。因此,要使其正常工作,您可以:

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

4
请注意,异步操作可能在用户按下键盘和 Console.ReadLine() 完成之前尚未完成。如果你没有显式地在 Task 上使用 Wait 方法,应用程序可能会在异步操作完成之前终止。请确保对任务进行等待以避免出现此问题。 - Johnathon Sullinger

5

我通常采取的方法是让Main方法调用一个返回TaskRun()方法,然后使用.Wait()等待Task完成。

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()时遇到死锁风险。


2
这将使用Wait()阻止任务,这不再是正确的异步上下文处理方式。请在此处阅读另一个问题。链接 - Pimenta
4
您提供的链接是用于MVC应用程序,此处您是正确的。在C#7.2之前,在控制台应用程序中必须执行此操作,否则控制台应用程序会在异步操作完成之前结束执行并关闭。在控制台应用程序中不需要担心任何SyncContext。然而,C#7.2允许您的Main方法返回一个async Task,因此您可以在控制台应用程序中进行等待。 - Johnathon Sullinger

3

代码修复,例如:

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

-2
在函数内运行任务,等待并检查异常,然后返回结果。
Func<HttpResponseMessage> myFun = () => 
{
   var t = Task.Run(async () => await myTask);
   t.Wait();
   if (t.IsFaulted)
      throw t.Exception;
   return t.Result;
};

为什么这个被投票否决了?我正在尝试学习异步编程,知道这个会对我很有帮助。 - Eric
2
@Eric - 因为它使用的是Task.Run()机制,这并不是技术上的“async”。它更像是一个并行/并发结构。关于异步和并行的一般共识是:异步 -> I/O操作(例如数据库、磁盘等访问) 并行 -> CPU密集型任务,例如在大整数上计算斐波那契数列 - bbqchickenrobot
非常有帮助。我正在进行高性能计算,一直找不到好的模式,可能是因为我一直在搜索异步而不是并行计算。 - Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接