【问题标题】:async/await runs synchronously?async/await 同步运行?
【发布时间】:2019-07-10 21:05:14
【问题描述】:

以下代码来自https://blog.stephencleary.com/2012/02/async-and-await.html我只是添加了一些描述性方法

class Program
{
    async static Task Main(string[] args)
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 1 here
        await DoSomethingAsync();
        Console.WriteLine("do other workzzz");
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);  //thread id is 4 here
        Console.ReadLine();
    }

    public static async Task DoSomethingAsync()
    {
        await Task.Delay(2000);    
    }
}

作者说:

我喜欢将“等待”视为“异步等待”。也就是说,async 方法会暂停,直到 awaitable 完成(所以它等待),但实际线程没有被阻塞(所以它是异步的)。

所以我的问题是: 为什么实际线程(在我的情况下是线程 id 1)没有被阻塞? 从我的角度来看,线程被阻塞是因为在当前方法完成之前不会执行进一步的语句。我们可以看到Console.WriteLine("do other workzzz");直到DoSomethingAsync()完成后才会被执行,是不是有点阻塞?

另外需要注意的重要一点是,DoSomethingAsync(); 完成后,线程 id 从 1 变为 4,不再有“实际线程”。为什么线程1消失了?不应该是线程4消失吗?

【问题讨论】:

  • @AllanS.Hansen 这不是这篇文章的真正目的
  • 使用await Task.WhenAll(taskA, taskB, …); 一次等待多个任务。
  • @ckuri 我只想知道 B 部分是否可以同步调用,也就是 async/await 也可以同步运行。
  • @slowjams 阅读了blog of Stephen Cleary 以及所有关于 async/await、Tasks 等的文章——其中有很多,每一篇都值得一读。这应该回答你所有的异步/等待问题

标签: c# .net async-await


【解决方案1】:

我认为 B 部分中缺少变量赋值让您感到困惑。

B 部分可以改写为:

var a = Task.Delay(1000);
await a;

var b = Task.Delay(1000);
await b;

var c = Task.Delay(1000);
await c;

etc...

如果您在开始时调用所有任务并在结束时等待所有任务(如 A 部分),那么您可以将其视为并行运行的那些任务。

await 表示在继续之前等待任务(在本例中为 Task.Delay(1000))完成。 没有await,你可以认为任务在后台运行(但主程序可以在完成之前退出)

要等到所有任务完成后再退出程序,您可以执行以下操作: await Task.WhenAll(taskA, taskB, …);

【讨论】:

    【解决方案2】:

    异步操作的处理方式与其他方法不同。

    简化解释:您的代码..

    async static Task Main(string[] args) 
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId); //thread id is 1 here
        await DoSomethingAsync();
        Console.WriteLine("do other workzzz");
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);  //thread id is 4 here
        Console.ReadLine(); 
    }
    

    按如下方式执行:

    调用线程执行..

    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    

    await 语句等待的代码被分派到任务调度程序,该任务调度程序使用同步上下文来排队、调度和执行它。同步上下文的实现负责决定这段代码在哪个线程中执行。

     await DoSomethingAsync();
    

    await 之后的代码被回发到开始时捕获的同步上下文中。同样,根据实现,其他线程可能会执行它。

    Console.WriteLine("do other workzzz");
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
    Console.ReadLine(); 
    

    即使代码同步读取,async/await 模式也会中断流程。

    【讨论】:

    • "await 语句被调度到任务调度器,它使用同步上下文来排队、调度和执行它。"并不真地。调用DoSomethingAsync() 方法,返回一个任务。然后await 运算符生效:如果该任务已经完成,则继续正常执行。否则,将在任务已完成 时安排继续。您编写它的方式听起来像DoSomethingAsync() 是“调度到任务调度程序”。这只是延续。
    • @JonSkeet 已更新。我是否正确将继续回发到捕获的同步上下文?
    • 默认情况下,使用Task,是的 - 但它真的取决于等待的功能。所以ConfigureAwait(false) 返回一个这样做的awaitable。
    【解决方案3】:

    (最初的问题是这样的:“当你等待异步方法而不是让它并行运行时,你怎么称呼它?”)

    我会使用“顺序”这个词。他们按顺序一次运行一个。但是这种类型的任务(async)仍然是异步的,因为它的调度和执行方式不同。决定它是否是异步的不仅仅是调用者。 (另外,调用者是async,它的调用者也会是async,所以你真的不能同步调用一个异步任务。(如果你尝试,使用像Wait()这样的函数,你有死锁的风险。因为在引擎盖,它与同步函数不同。)

    【讨论】:

    • 那么是什么让异步函数在调度和执行方面异步?
    • @slowjams 在 .NET 端,有一个任务调度器决定运行什么。它将任务分配给线程并在可以暂停的点启动/暂停/停止任务。在操作系统方面,.NET 任务可以暂停,直到操作系统为它们提供新的东西(例如,如果读取或写入完成)。我对细节很模糊,但是这里描述了 .NET 方面的内容(非常详细):docs.microsoft.com/en-us/dotnet/standard/…
    • @slowjams: what makes asynchronous function asynchronous 它返回给它的调用者。见我的async intro
    • @StephenCleary 我阅读了您的博客并编辑了我的问题,请您再看看好吗?
    • @slowjams:您几乎完全改变了问题,现在大多数答案都没有意义。 Stack Overflow 是一个问答网站,因此如果您有重大更改,最好提出一个新问题。
    【解决方案4】:

    为什么实际线程(在我的例子中是线程 id 1)没有被阻塞?

    await 用于未完成的等待对象时,await async 方法返回。调用线程继续执行下一段代码。

    我们可以看到 Console.WriteLine("do other workzzz");在 DoSomethingAsync() 完成之前不会执行,这不是一种阻塞吗?

    它不会阻塞线程。该方法在await 处“暂停”,但没有线程将其固定到位。

    例如,在await 之前检查您的调用堆栈,在await 之后再次检查。您会看到调用堆栈没有保留,因为async 方法返回并且原始线程继续运行。在await 之后,该调用堆栈不会重新进入。

    另一个需要注意的重要事情是,在 DoSomethingAsync(); 之后完成,线程 id 从 1 变为 4,不再有“实际线程”。为什么线程1消失了?不应该是线程4消失吗?

    线程 1 和 4 都是实际线程。线程 1 是主线程,即启动控制台应用程序的线程。线程 4 是线程池线程。

    DoSomethingAsync 完成并且await 准备好恢复其async 方法时,它必须在某处恢复它。默认情况下,await 将捕获“上下文”;对于控制台应用程序,这是线程池上下文。所以当async方法恢复时,它会在线程池线程上恢复。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-25
      • 1970-01-01
      • 1970-01-01
      • 2019-10-31
      • 1970-01-01
      相关资源
      最近更新 更多