【问题标题】:Waiting for something to happen - asynchronous or synchronous model?等待某事发生 - 异步或同步模型?
【发布时间】:2014-03-07 08:42:55
【问题描述】:

我有这个方法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. 上面的什么是等待某事发生然后继续的正确方法?

【问题讨论】:

  • 很可能应该有某种外部事件,发生在“阅读器到达”时,例如用户输入、套接字消息、传入的串行端口数据等。有类似的吗?
  • 没有。我正在编写的库会公开此事件并在遇到上述代码时引发。
  • 您使用的代码是busy waiting loop 的略微改进版本,它会轮询带有节流的东西。 如果您没有任何其他方式来获得更改通知,那么使用await Task.Run 将此类循环卸载到池线程可能是您能做的最好的事情。
  • 其实你可以用Task.Delay来改进一下,比如this
  • @Noseratio:我刚问过,答案是肯定的。似乎有一个 UI 存在并等待在smart card arrival 上得到通知。正如你所指出的,如果我没有使用 await,我的 UI 将被冻结。

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


【解决方案1】:

我上面采用的异步模型真的没用吗? 为什么她要我把那条线改成正常的同步方法 对我来说仍然是一个问题。

您使用的代码是the busy waiting loop 的略微改进版本,它正在轮询具有节流功能的东西。 如果您没有任何其他方式来获得更改通知,您可以像使用 await Task.Run 一样将此循环卸载到池线程,或者更好的是,使用 @987654325 @:

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

【讨论】:

  • 有问题的代码可能没有在 UI 线程上运行。
  • @svick,OP 已经澄清它是一个 UI 线程,但在非 UI 线程的情况下,我仍然坚持使用 await Task.Delay(1000),除非轮询间隔必须非常小。跨度>
【解决方案2】:

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

从其他并发活动的角度来看,它们的区别再大不过了。在“等待”情况下,线程不会阻塞,而在第二个同步情况下它会阻塞。使用哪个决定取决于您在此处未透露的其他因素。

如果这是 UI 线程,您很可能不希望它阻塞。使用“等待”。

如果这是一个专用的工作线程,您很可能确实希望它阻塞。使用同步形式。

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

另外,请考虑避免“忙睡眠”等待。做这种事情通常是一种糟糕的方式。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      您可以将延续(也称为回调)传递给您的过程:

      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() 会冻结您的应用程序。考虑基于事件的方法。

      【讨论】:

      • 在可以使用await 时使用延续通常是个坏主意。 await 使代码更加简洁易懂。
      • 是的,但你仍在使用Task。他的任务(双关语)是将代码转换为纯同步代码。我提出的解决方案基本上是一种事件模式,但没有使用 C# 事件。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2018-09-13
      • 2016-04-26
      • 1970-01-01
      相关资源
      最近更新 更多