C# 如何异步执行同步方法并等待结果
我的应用程序通过TCP/IP协议和COM端口连接到许多外部设备。主逻辑在C# 如何异步执行同步方法并等待结果,c#,asynchronous,async-await,synchronous,begininvoke,C#,Asynchronous,Async Await,Synchronous,Begininvoke,我的应用程序通过TCP/IP协议和COM端口连接到许多外部设备。主逻辑在MainController类中(作为有限状态机实现),它侦听来自外部设备的信号并向它们发送命令 每当MainController接收到外部信号时,它就会向GUI通知一个事件,例如OnSensor1Received,OnSensor2Received,。。。所以我可以显示一个图标或消息或任何东西。同样的逻辑也适用于其他方向-当MainController向外部设备发送信号时,会引发一个事件并在GUI中显示一些内容 在此之前,
MainController
类中(作为有限状态机实现),它侦听来自外部设备的信号并向它们发送命令
每当MainController
接收到外部信号时,它就会向GUI通知一个事件,例如OnSensor1Received
,OnSensor2Received
,。。。所以我可以显示一个图标或消息或任何东西。同样的逻辑也适用于其他方向-当MainController
向外部设备发送信号时,会引发一个事件并在GUI中显示一些内容
在此之前,所有通信都是异步的(如果这是正确的术语?)。这意味着我接收事件并处理它们,我发出命令,但在同一操作中从不等待返回(可以说所有发送命令的方法都是无效的)
但现在我必须添加新设备,它只有一个公共方法int进程(字符串输入,输出字符串输出)
。该方法的执行可能需要几秒钟的时间,同时所有其他代码都在等待-例如,如果在执行该方法的过程中,我从另一个外部设备收到信号,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的本机支持。您可以使用老式的via
BeginInvoke
/EndInvoke
和捕获的变量:
// 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);
//通知GUI阻塞插件已激活。
OnBlockingPluginActive(此为EventArgs.Empty);
字符串结果;
Func processAsync=()=>blockingPlugin.Process(输入,输出结果);
processAsync.BeginInvoke(ar=>
{
var status=processAsync.EndInvoke(ar);
如果(状态==0)
{
//好的,通知GUI并在有限状态机中发出BlockingPluginOK命令。
OnBlockingPluginOK(这个,EventArgs.Empty);
}
其他的
{
//错误,通知GUI并在有限状态机中发出BlockingPluginError命令。
OnBlockingPlugineError(此为EventArgs.Empty);
}
},空);
每当MainController接收到外部信号时,它就会向GUI通知一个事件
在此之前,所有通信都是异步的
它已经是异步的了。它只是使用事件来通知完成,而不是任务
或IObservable
。普通事件确实会使代码更混乱(即,您的代码必须分布在许多方法上,并显式维护自己的状态对象),但它们实际上是异步的
如何使Write
方法异步执行,以便GUI实时显示所有其他信号
好吧,你不能“让它异步运行”——无论如何,这不是为了“异步”的正确含义。目前,Write
方法阻止调用线程(即,它是同步的)。没有一种调用方法可以神奇地阻止它阻塞调用线程(即异步)
但是,您可以使用我称之为“伪异步”的技术。本质上,UI线程通过在线程池线程上执行该操作来假装该操作是异步的。该操作仍在阻塞,但它会阻塞线程池线程而不是UI线程
如果可以使用,则可以编写如下代码:
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);
您可以尝试这样做:如果您仍在使用.NET 4.0,只需使用Task.Factory.StartNew并使用.ContinueWith“几秒钟”是不合理的行为。您可以将调用移动到工作线程中,但您只需将其喷射并仍然阻塞即可。要么是该库彻底崩溃,要么是由于环境问题,它现在无法正常运行。如果你试图解决这个问题,你不会做得更好,请向图书馆的作者征求意见。谢谢你的解释。你能对@Ivan Stoev的解决方案()发表评论吗?我先试用过,看起来还可以。@sventevit:
BeginInvoke
也将使用线程池线程;但是,该代码示例将在线程池线程而不是UI线程上引发OnBlockingPluginOK
和OnBlockingPluginError
。@sventevit:我通常不会这么说。谢谢,这正是我要找的。