等待事情发生 - 异步或同步模型?

时间:2014-02-10 06:00:06

标签: c# .net multithreading .net-4.5 async-await

我有这个方法WaitForReaderArrival,如下所示: (等待读者到达的所有时间)

        public void WaitForReaderArrival()
        {
            do
            {
                if (ReaderArrived())
                {
                    break;
                }

                System.Threading.Thread.Sleep(1000);
            } while (ReaderArrived() == false);
        }

我正在等待读者使用,

 await Task.Run(new Action(WaitForReaderArrival));
 if (ReaderArrived())
 {
      //Raise an ReaderArrived here!
    ..//blah blah
 }

我的一位同事让我把上面一行改为

 WaitForReaderArrival(); 
 if (ReaderArrived())
 {
    //Raise an ReaderArrived here!
    ..//blah blah
 }

问题是:

  1. 我上面采用的异步模型是不是真的有用?为什么她要我将这一行更改为正常的同步方法仍然是一个问题。

  2. 以上是等待事情发生然后继续的正确方法?

4 个答案:

答案 0 :(得分:5)

  

我上面采用的异步模型是不是真的有用?   她为什么要我将这一行改为普通的同步方法   对我来说仍然是一个问题。

您正在使用的代码是the busy waiting loop的稍微改进版本,它会对具有限制功能的内容进行轮询。 如果您没有任何其他方式获得更改通知,您可以将此循环卸载到池线程,就像您已经使用await Task.Run一样,或者更好的是,使用Task.Delay

public async Task WaitForReaderArrivalAsync()
{
    while (!ReaderArrived())
    {
        await Task.Delay(1000).ConfigureAwait(false);
    }
}
  

我的一位同事让我改变了上面这句话......   以上是等待事情发生的正确方法   继续?

你的同事错了。如果您使用WaitForReaderArrival来调用原始await Task.Run,或者将上面提出的版本称为WaitForReaderArrivalAsync().Wait(),则会阻止UI线程。为了保持UI线程消息循环的功能,您应该创建代码"Async All the Way"

// top-level event handler
async void Button_Click(object sender, EventArgs e)
{
    await WaitForReaderArrivalAsync();
    MessageBox.Show("ReaderArrived!");
}

这是调用它的正确方法。从概念上讲,它与在计时器事件上检查ReaderArrived非常相似,但async/await为您提供了方便的线性伪同步代码流。

注意,有一个流行的反模式,它忙于等待DoEvents以保持UI响应,有效地在UI线程上创建嵌套的消息循环:

public void WaitForReaderArrival()
{
    while (!ReaderArrived())
    {
        Application.DoEvents();
        System.Threading.Thread.Sleep(100);
    }
}

这样做是错误的:Keeping your UI Responsive and the Dangers of Application.DoEvents

答案 1 :(得分:2)

从您的代码的角度来看,没有区别。执行将在此时停止,直到相关事件发生后才会恢复。

从其他并发活动的角度来看,它们之间的差别不大。在'await'情况下,线程不会阻塞,在第二个同步情况下它会阻塞。关于使用哪个因素的决定取决于您未在此处泄露的其他因素。

如果这是UI线程,您很可能不希望它阻止。使用'await'。

如果这是一个专用的工作线程,你很可能希望它阻止。使用同步表单。

我必须指出,在严格的分析中,第二个if (ReaderArrived())是错误的。它应该是一个断言,因为否则没有什么有用的。

另外,请考虑避免“忙碌的睡眠”等待。这通常是做这种事情的坏方法。

最后,你真的必须习惯于在来到这里之前先与同事交谈。 :)

答案 2 :(得分:0)

回答你的第二点 基本上我建议不要使用上面的代码使用事件,如果你的意图是在发生另一个事件时做的事情。

答案 3 :(得分:0)

  

以上是等待事情发生的正确方法   然后继续?

您可以将延续(a.k.a.回调)传递给您的程序:

public void WaitForReaderArrival(Action callback)
{
    do
    {
        if ( ReaderArrived() )
        {
            break;
        }

        System.Threading.Thread.Sleep(1000);
    } while ( ReaderArrived() == false );

    callback();
}

用法示例:

WaitForReaderArrival(() =>
    {
        // Raise an ReaderArrived here!
        // ...blah blah
    });

  

我上面采用的异步模型是不是真的有用?   她为什么要我将这一行改为普通的同步方法   对我来说仍然是一个问题。

重点是,在您的应用程序的某个地方,您必须等待Reader到达。即使你在另一个后台线程中进行等待,你仍然需要等待该线程完成。

我唯一担心的是在UI线程上使用Thread.Sleep()会冻结您的应用程序。考虑一种基于事件的方法。