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

问题描述 投票:0回答:2

我花了一些时间弄清楚如何在 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() 有效,而 wait 不在操作委托中”的“奖励”

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

c# asynchronous lambda async-await task
2个回答
3
投票

最后一种方法的问题在于,您用

Action
包装了异步 lambda,它描述了
void
返回函数。

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

此时委托的调用者无法等待内部异步函数及其延迟,因此这里我们处于

async void
调用的上下文中,其中操作最终将完成,但没有
Task-like 
返回实例。由于调用者无法等待函数终止,因此它会继续执行,直到
Main
方法退出。

一般来说,

async void
应该仅用于异步事件处理程序,因为正如您所见,不允许调用者正确等待函数完成。

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

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

1
投票
// 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.

^^ 请参阅代码中的注释。如果您在

foo3.Invoke();
之后放置任何阻止程序退出至少一秒的内容,您将看到“After 4 Delay”输出。

这基本上是一个

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

请注意,这还有其他副作用。例如,如果操作中出现异常。


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"

虽然这“有效”,但这是不好的做法。应考虑(并等待)

Func<Task>

© www.soinside.com 2019 - 2024. All rights reserved.