线程的等待/异步有多么不同?

时间:2012-11-17 09:24:00

标签: c# .net multithreading async-await

我正在尝试熟悉c#的新await / async关键字,我发现了几个我无法理解的方面。

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

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

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

    第二件事是异步函数如果不包含await运算符则同步执行。而且,根据microsoft文档,异步函数总是在调用者线程中执行(与BeginInvoke相比)。这让我想到了3个下一个问题:

  2. 异步函数在释放给调用函数之前执行多少次?

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

    在我读过的关于await / async的文章中,有人说没有await运算符的异步函数按顺序执行,并且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在异步函数之外无效。为什么呢?

3 个答案:

答案 0 :(得分:16)

我建议您阅读我的async intro

  

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

没有。您需要awaitWriteAsync的调用。

  

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

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

  

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

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

作为一般规则,请勿在{{1​​}}方法中使用任何阻止方法。

  

如果我在其中一个异步函数中使用semaphore.Wait(),并且它希望信号量由其他异步函数释放,该怎么办?这会像线程一样运行,还是会导致死锁?

这完全取决于你的背景。 async是一种阻止方法,因此如果“其他”Wait方法需要被阻止方法保存的上下文,那么您将陷入僵局。

请注意asyncSemaphoreSlim - 友好;您可以使用async代替WaitAsync

  

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

因为Wait关键字启用了async关键字。这样做是为了最大限度地减少新关键字对C#语言和代码可读性的影响。

答案 1 :(得分:15)

您可以在以下post by Eric Lippert中找到有关await运算符的问题的答案,其中他说:

  

“await”运算符......并不意味着“此方法现在阻止了   当前线程,直到异步操作返回“。那会   将异步操作恢复为同步   操作,这正是我们试图避免的。相反,   它意味着与此相反;它意味着“如果我们正在等待任务   还没有完成,然后注册这个方法的其余部分   继续该任务,然后立即返回给您的呼叫者;   任务将在完成时调用continuation。 - Eric Lippert

答案 2 :(得分:0)

简而言之,似乎答案是&#34;非常&#34;。虽然以下内容可能无法回答您的所有问题,但我认为它适用于一般情况下的用例。想想你的Stream而不是他们所指的网络IO。

  

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

     

这意味着我们等待着#34;完成后,线程不会被消耗掉。 [...]

     

可以在1个线程上执行所有操作。 [...]然而,它将取决于平台,&#34; awaiter&#34;实施&amp;正在使用的同步上下文。

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