如何异步执行同步方法并等待结果

时间:2016-07-24 09:39:11

标签: c# asynchronous async-await synchronous begininvoke

我的应用程序通过TCP / IP协议和COM端口连接到许多外部设备。主逻辑位于MainController类(实现为有限状态机),它侦听来自外部设备的信号并向它们发送命令。

每当MainController收到外部信号时,它会通过事件通知GUI,例如OnSensor1ReceivedOnSensor2Received,......所以我可以显示图标或消息等等。相同的逻辑适用于其他方向 - 当MainController向外部设备发送信号时,会引发事件并在GUI中显示某些内容。

直到这里所有通信都是异步的(如果这是正确的术语?)。这意味着我接收事件并处理它们并且我触发命令但从不等待同一操作中的返回(你可以说发送命令的所有方法都是无效的。)

但现在我必须添加新设备,它只有一个公共方法int Process(string input, out string output)。该方法的执行可能需要几秒钟,同时所有其他代码都在等待 - 例如,如果在执行此方法期间我从另一个外部设备获得信号,则GUI将仅在阻塞后刷新方法Write执行。如何使Write方法异步执行,以便GUI实时显示所有其他信号?

代码示例,当我尝试使用新设备时,会在MainController中的某处调用:

// Notify GUI that the blocking plugin was activated.
OnBlockingPluginActive(this, EventArgs.Empty);

string result;
// Here is the problem - during the execution of this method the GUI is not responding for other signals.
var status = blockingPlugin.Process(input, out result);
if (status == 0)
{
    // OK, notify GUI and fire a BlockingPluginOK command in finite state machine.
    OnBlockingPluginOK(this, EventArgs.Empty);
}
else
{
    // Error, notify GUI and fire a BlockingPluginError command in finite state machine.
    OnBlockingPluginError(this, EventArgs.Empty);
}

另请注意,我使用的是.net 4.0,无法升级到4.5,因此没有对async / await的原生支持。

2 个答案:

答案 0 :(得分:1)

您可以通过BeginInvoke / EndInvokeAsyncCallback一起使用旧版Asynchronous delegate call并获取变量:

// Notify GUI that the blocking plugin was activated.
OnBlockingPluginActive(this, EventArgs.Empty);

string result;
Func<int> processAsync = () => blockingPlugin.Process(input, out result);
processAsync.BeginInvoke(ar =>
{
    var status = processAsync.EndInvoke(ar);
    if (status == 0)
    {
        // OK, notify GUI and fire a BlockingPluginOK command in finite state machine.
        OnBlockingPluginOK(this, EventArgs.Empty);
    }
    else
    {
       // Error, notify GUI and fire a BlockingPluginError command in finite state machine.
       OnBlockingPluginError(this, EventArgs.Empty);
    }
}, null);

答案 1 :(得分:1)

  

每当MainController收到外部信号时,它会通过事件向GUI通知......

     

...

     

直到这里所有的通信都是异步的......

它已经是异步的。它只是使用事件来通知完成而不是drusba :: a -> VoidTask。普通事件确实会消耗更多的代码(即,您的代码必须分布在许多方法上并显式维护自己的状态对象),但它们实际上是异步

  

如何使IObservable方法异步执行,以便GUI实时显示所有其他信号?

嗯,你不能“让它以异步方式运行” - 无论如何都不是“异步”的正确含义。目前,Write方法阻塞调用线程(即,它是同步的)。没有办法调用那种神奇地阻止它阻塞调用线程的方法(即异步)。

但是,您可以使用我称之为“假异步”的技术。本质上,UI线程假装操作是异步的,通过在线程池线程上执行它。该操作仍然是阻塞的,但它阻塞了线程池线程而不是UI线程。

如果您可以使用Microsoft.Bcl.Async,那么您可以编写如下代码:

Write

否则,你将不得不做旧学校:

try
{
  var result = await TaskEx.Run(() =>
  {
    string result;
    if (blockingPlugin.Process(input, out result) != 0)
      throw new Exception("Blocking plugin failed.");
    return result;
  });
  OnBlockingPluginOK(this, EventArgs.Empty);
}
catch
{
  OnBlockingPluginError(this, EventArgs.Empty);
}