【问题标题】:Right way to create and call lambda of async methods in C# [duplicate]在 C# 中创建和调用异步方法的 lambda 的正确方法 [重复]
【发布时间】:2021-08-13 19:56:06
【问题描述】:

我很难弄清楚如何在 C# 中“存储”和调用异步 lambda,它基本上是通过运行来尝试的。我发现了一些可行的方法,而另一些则不可行(见下文),我读过它,但仍然不清楚为什么有些可行而有些不可行,有人可以解释为什么并添加更好的方法吗?

public static class Program
{
    static async Task Main(string[] args)
    {
        // works fine, everything is printed
        Console.WriteLine("Before 1 delay");
        await Task.Delay(1000);
        Console.WriteLine("After 1 delay");

        // works fine, everything is printed
        var foo = new Func<Task>(async () =>
        {
            Console.WriteLine("Before 2 delay");
            await Task.Delay(1000);
            Console.WriteLine("After 2 delay");
        });
        await foo.Invoke();

        // works fine, everything is printed
        var foo2 = new Action(() =>
        {
            Console.WriteLine("Before 3 delay");
            Task.Delay(1000).Wait();
            Console.WriteLine("After 3 delay");
        });
        foo2.Invoke();

        // Does not work, just before is printed
        var foo3 = new Action(async () =>
        {
            Console.WriteLine("Before 4 delay");
            await Task.Delay(1000);
            Console.WriteLine("After 4 delay");
        });
        foo3.Invoke();
    }
}

请注意,这个问题与下面的问题不同,即使答案相同,上下文也不同,因为这个问题基本上是为什么 Action 委托确实运行异步,即使等待在那里,除了有“为什么 .Await() 有效,而 await 不要在动作委托中”的“奖励”

异步任务与异步无效 “await” 不等待调用完成 为什么控制台窗口在显示我的输出后立即关闭? 如何阻止 C# 控制台应用程序自动关闭?

【问题讨论】:

  • 我很好奇为什么你会首先将Func/Action 分配给一个变量,而不是内联使用它们并让类型推断来完成它的工作。在您当前的状态下,您不妨将它们定义为类上的静态函数或使用本地函数。
  • foo3.Invoke(); 之后放置一个“Console.ReadKey()” -> 看看会发生什么。
  • 实际上我的 Ide 抱怨说无法推断,我不确定在 foo 的情况下推断是否可以在这里工作......
  • @Andre: Here you go.

标签: c# asynchronous lambda async-await task


【解决方案1】:

您最后一种方法的问题是您使用 Action 包装了一个异步 lambda,它描述了一个 void-returning 函数。

await Task.Delay(1000); 执行时,它告诉运行时在延迟异步完成后安排继续(即该行之后的指令)。

此时委托的调用者无法等待内部异步函数及其延迟,所以这里我们处于async void 调用的上下文中,其中操作最终会完成,但不会返回 Task-like 实例。由于调用者无法等待函数终止,它会继续执行,直到Main 方法退出。

一般来说,async void 应该只用于asynchronous event handlers,因为如您所见,它不允许调用者正确地等待函数完成。

在 C# 中创建和调用异步方法的 lambda 的正确方法

// return a Task!
var wrapper = new Func<Task>(async () =>
{
    await SomethingAsync();
});
await wrapper.Invoke();

【讨论】:

    【解决方案2】:
    // Does not work, just before is printed
    // Action-Delegate => "void" does not return the Task, cannot be awaited.
    var foo3 = new Action(async () =>
    {
        Console.WriteLine("Before 4 delay");
        await Task.Delay(1000); // Meanwhile, your Program ends!
        Console.WriteLine("After 4 delay");
    });
    foo3.Invoke(); // <- Starts the action "fire&forget"
    //Your Program ends BEFORE second WriteLine can be executed.
    
    

    ^^ 见代码中的 cmets。如果您在 foo3.Invoke(); 之后放置任何内容,阻止您的程序至少再退出一秒钟,您将看到“After 4 delay”输出。

    这基本上是一个async void Method() - 这是一个坏主意,除了异步事件处理程序。

    请注意,这也有其他副作用。例如,在 Action 内部出现异常的情况下。


    var foo2 = new Action(() =>  // _NOT_ async!
    {
        Console.WriteLine("Before 3 delay");
        Task.Delay(1000).Wait();  // Blocking!
        Console.WriteLine("After 3 delay");
    });
    foo2.Invoke(); // Will block. => "works"
    

    【讨论】:

    • 所以有人认为两个答案都没有帮助,也懒得告诉我们原因。请这样做,@downvoter:如果可以改进答案,请帮助我们这样做!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 2021-02-09
    • 2019-12-18
    • 2011-04-15
    相关资源
    最近更新 更多