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

C# 如何异步执行同步方法并等待结果,c#,asynchronous,async-await,synchronous,begininvoke,C#,Asynchronous,Async Await,Synchronous,Begininvoke,我的应用程序通过TCP/IP协议和COM端口连接到许多外部设备。主逻辑在MainController类中(作为有限状态机实现),它侦听来自外部设备的信号并向它们发送命令 每当MainController接收到外部信号时,它就会向GUI通知一个事件,例如OnSensor1Received,OnSensor2Received,。。。所以我可以显示一个图标或消息或任何东西。同样的逻辑也适用于其他方向-当MainController向外部设备发送信号时,会引发一个事件并在GUI中显示一些内容 在此之前,

我的应用程序通过TCP/IP协议和COM端口连接到许多外部设备。主逻辑在
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:我通常不会这么说。谢谢,这正是我要找的。