Async/Await 单 thread/some 线程

Async/Await single thread/some threads

我需要一些关于正确使用 await 的规则。 运行 .net core c# 7.2 中的这段代码:

static class Program
{
    static async Task<string> GetTaskAsync(int timeout)
    {
        Console.WriteLine("Task Thread: " + Thread.CurrentThread.ManagedThreadId);
        await Task.Delay(timeout);
        return timeout.ToString();
    }

    static async Task Main()
    {
        Console.WriteLine("Main Thread: " + Thread.CurrentThread.ManagedThreadId);

        Console.WriteLine("Should be greater than 5000");
        await Watch(NotParallel);
        Console.WriteLine("Should be less than 5000");
        await Watch(Parallel);
    }

    public static async Task Parallel()
    {
        var res1 = GetTaskAsync(2000);
        var res2 = GetTaskAsync(3000);

        Console.WriteLine("result: " + await res1 + await res2);
    }

    public static async Task NotParallel()
    {
        var res1 = await GetTaskAsync(2000);
        var res2 = await GetTaskAsync(3000);

        Console.WriteLine("result: " + res1 + res2);
    }

    private static async Task Watch(Func<Task> func) {
        var sw = new Stopwatch();
        sw.Start();

        await func?.Invoke();

        sw.Stop();
        Console.WriteLine("Elapsed: " + sw.ElapsedMilliseconds);
        Console.WriteLine("---------------");
    }
}

如您所见,两种方法的行为是不同的。实践中很容易出错。所以我需要一个 "thumb rule".

真男人更新拜托,运行代码。请解释为什么 Parallel() 运行s 比 NonParallel() 快。

在没有 await 的情况下调用 GetTaskAsync 时,您实际上得到了一个任务,其中包含要执行的方法(即 GetTaskAsync)。但是当调用 await GetTaskAsync 时, 执行暂停,直到方法执行完毕,然后你得到结果。

让我说得更清楚:

var task = GetTaskAsync(2000);

此处,任务类型为Task<string>

var result = await GetTaskAsync(2000);

这里的结果是 string.

类型

所以要解决您的第一个问题:何时等待您的任务实际上取决于您的执行流程。

现在,至于为什么 Parallel() 更快,我建议您阅读此 article(所有内容都很有趣,但对于您的具体示例,您可以跳转到 Tasks return"hot").

现在让我们分解一下:

The await keyword serves to halt the code until the task is completed, but doesn't actually start it.

在您的示例中,NotParallel() 将花费更长的时间,因为您的任务按顺序执行,一个接一个。如文章所述:

This is due to the tasks being awaited inline.

然而在Parallel()中...

the tasks now run in parallel. This is due to the fact that all [tasks] are started before all [tasks] are subsequently awaited, again, because they return hot.

关于 'hot' 个任务

我建议您阅读以下内容:Task-based Asynchronous Pattern (TAP)

任务状态部分在这里很有趣,可以了解冷任务和热任务的概念:

Tasks that are created by the public Task constructors are referred to as cold tasks, because they begin their life cycle in the non-scheduled Created state and are scheduled only when Start is called on these instances.

All other tasks begin their life cycle in a hot state, which means that the asynchronous operations they represent have already been initiated

我邀请您广泛阅读 async/awaitTasks。除了我上面提供的资源之外,这里还有一些资源:

Asynchronous Programming in C# 5.0 part two: Whence await?

Async/Await - Best Practices in Asynchronous Programming

Async and Await