如何异步同步执行方法并等待结果
本文关键字:方法 执行 等待 结果 同步 何异步 异步 | 更新日期: 2023-09-27 17:58:33
我的应用程序通过TCP/IP协议和COM端口连接到许多外部设备。主逻辑在MainController
类(实现为有限状态机)中,它侦听来自外部设备的信号并向它们发送命令。
每当MainController
接收到外部信号时,它就会向GUI通知事件,例如OnSensor1Received
、OnSensor2Received
,。。。这样我就可以显示一个图标或消息或其他什么。同样的逻辑适用于其他方向——当MainController
向外部设备发送信号时,会引发一个事件,并在GUI中显示一些内容。
在此之前,所有的通信都是异步的(如果这是正确的术语?)。这意味着我接收事件并处理它们,并发出命令,但从不在同一操作中等待返回(可以说所有发送命令的方法都是无效的)。
但现在我必须添加新的设备,它只有一个公共方法int Process(string input, out string output)
。该方法的执行可能需要几秒钟的时间,同时所有其他代码都在等待——例如,如果在执行该方法的过程中,我从另一个外部设备收到信号,则只有在执行阻塞方法Write
之后,GUI才会被刷新。如何使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的本地支持。
您可以通过BeginInvoke
/EndInvoke
与AsyncCallback
和捕获的变量一起使用老式的异步委托调用:
// 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);
每当MainController接收到外部信号时,它就会向GUI通知一个事件。。。
在此之前,所有的通信都是异步的。。。
它已经是异步的了。它只是使用事件来通知完成,而不是使用Task
或IObservable
。普通事件确实会使代码更加混乱(即,代码必须分布在许多方法上,并显式地维护自己的状态对象),但它们实际上是异步的。
如何使
Write
方法异步执行,以便GUI实时显示所有其他信号?
好吧,你不能"让它异步运行"——无论如何,这不是"异步"的正确含义。目前,Write
方法阻塞调用线程(即,它是同步的)。没有一种方法可以调用该方法,从而神奇地防止它阻塞调用线程(即异步)。
然而,您可以使用一种我称之为"伪异步"的技术。本质上,UI线程通过在线程池线程上执行操作来假装操作是异步的。该操作仍然处于阻塞状态,但它阻塞了线程池线程,而不是UI线程。
如果你可以使用Microsoft.Bcl.Async
,那么你可以写这样的代码:
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);
}
否则,你将不得不做老派:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
var task = Task.Factory.StartNew(() =>
{
string result;
if (blockingPlugin.Process(input, out result) != 0)
throw new Exception("Blocking plugin failed.");
return result;
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
task.ContinueWith(t => OnBlockingPluginOK(this, EventArgs.Empty),
TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.OnlyOnRanToCompletion,
ui);
task.ContinueWith(t => OnBlockingPluginError(this, EventArgs.Empty),
TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.NotOnRanToCompletion,
ui);