【问题标题】:How different is await/async from threading?等待/异步与线程有什么不同?
【发布时间】:2012-11-05 22:16:47
【问题描述】:

我正在尝试熟悉 c# 的新 await/async 关键字,但我发现了一些我不太了解的方面。

  1. 让我们从竞争条件开始:

    Stream s=...
    ...
    for(int i=0;i<100;i++)
    {
        s.WriteAsync(new byte[]{i},0,1);
    }
    

    这会一直按预期工作吗(例如写入文件 12345..... 而不是 13254 或其他东西)?

    第二件事是,如果async函数不包含await操作符,它会同步执行。而且,根据微软文档,异步函数总是在调用者线程中执行(与 BeginInvoke 相比)。这让我想到了接下来的 3 个问题:

  2. 异步函数在释放到调用者函数之前执行了多少?

    async void MyAsyncFunction()
    {
        Operation1();
        Operation2();
        Operation3();
        ....
        Stream s=...;
        await s.WriteAsync(....);
    }
    

    在我读过的关于 await/async 的文章中,据说没有 await 运算符的 async 函数是顺序执行的,而带有 async/await 的则立即返回。但令我烦恼的是,MyAsyncFunction 可能总是在释放之前执行 Operation1...Operation3,因为它击中await s.WriteAsync。

  3. 如果我像这样在异步函数中使用Thread.Sleep 会怎样:

    async void DoStuff()
    {
        Stream s=...;
        ...
        await s.WriteAsync(....);
        Thread.Sleep(10000);
        ....
    }
    

    Thread.Sleep 会阻塞执行它的整个线程还是仅仅阻塞异步函数?

  4. 如果我在其中一个异步函数中使用semaphore.Wait() 会怎样,它会期望信号量被另一个异步函数释放。这会像线程一样运行,还是会导致死锁?

  5. await 在异步函数之外不起作用。为什么?

【问题讨论】:

  • 您在一篇文章中问了太多问题 - 很难以连贯的方式回答所有问题。听起来您实际上应该做更多研究(例如,Visual Studio Async 主页上有大量信息),然后一次问一个特定问题。
  • 嗯。 async/await 是线程。只是更有效地使用线程。
  • @JohnSaunders 见 msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx : The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.
  • @JonSkeet 我实际上阅读了很多关于这方面的内容,这些是我在阅读文档后留下的问题。但可能是,毕竟我还没有找到正确的文档。你能指点我吗?
  • @AdrianCiura 我刚刚在上面的 cmets 中引用了它

标签: c# .net multithreading async-await


【解决方案1】:

我建议你阅读我的async intro。

这会一直按预期工作吗(例如写入文件 12345..... 而不是 13254 或其他什么)?

没有。您需要await 对WriteAsync 的调用。

异步函数在释放给调用者函数之前执行了多少?

直到它awaits 一个尚未完成的操作。

Thread.Sleep 会阻塞执行它的整个线程还是仅仅阻塞异步函数?

Thread.Sleep - 和所有其他阻塞方法 - 将阻塞 async 方法和正在执行它的线程。

作为一般规则,不要在async 方法中使用任何阻塞方法。

如果我在其中一个异步函数中使用 semaphore.Wait() 会怎样,它会期望信号量被另一个异步函数释放。这会像线程一样运行,还是会导致死锁?

这完全取决于您的上下文。 Wait 是一个阻塞方法,所以如果“其他”async 方法需要被阻塞方法持有的上下文,那么就会死锁。

注意SemaphoreSlim 对async 友好;您可以使用WaitAsync 代替Wait。

await 在异步函数之外不起作用。为什么?

因为async 关键字启用await 关键字。这样做是为了尽量减少新关键字对 C# 语言的影响和代码的可读性。

【讨论】:

    【解决方案2】:

    您可以在以下post by Eric Lippert 中找到有关awaitoperator 的问题的答案,他说:

    “等待”运算符……并不意味着“此方法现在阻止 当前线程,直到异步操作返回”。那会 将异步操作重新变为同步操作 操作,这正是我们试图避免的。相当, 它的意思与此相反;它的意思是“如果我们正在等待的任务 尚未完成然后将此方法的其余部分注册为 继续该任务,然后立即返回给您的呼叫者; 任务完成后将调用延续。 ——埃里克·利珀特

    【讨论】:

      【解决方案3】:

      简而言之,答案似乎是“非常”。虽然以下内容可能无法回答您的所有问题,但我认为它适用于足够普遍的用例。想想你的 Stream 而不是他们所指的网络 IO。

      在网络 IO 重叠的情况下,将使用 IO 完成端口,回调由硬件中断触发。

      这意味着当我们“等待”完成时,不会消耗线程。 [...]

      可以在 1 个线程上完成所有操作。 [...] 但是,这将取决于平台、“等待者”实现和正在使用的同步上下文。

      http://social.msdn.microsoft.com/Forums/en-US/async/thread/a46c8a54-f8d4-4fa9-8746-64d6a3a7540d/

      【讨论】:

      猜你喜欢
      • 2013-02-15
      • 2012-06-13
      • 1970-01-01
      • 2016-07-24
      • 1970-01-01
      • 2020-07-08
      • 2013-05-14
      相关资源
      最近更新 更多