asynchronous与“旧asynchronous委托”

我试图用一种新的语法来取代旧的“即发即忘”电话,希望能够更简单,而且似乎正在逃避我。 这是一个例子

class Program { static void DoIt(string entry) { Console.WriteLine("Message: " + entry); } static async void DoIt2(string entry) { await Task.Yield(); Console.WriteLine("Message2: " + entry); } static void Main(string[] args) { // old way Action<string> async = DoIt; async.BeginInvoke("Test", ar => { async.EndInvoke(ar); ar.AsyncWaitHandle.Close(); }, null); Console.WriteLine("old-way main thread invoker finished"); // new way DoIt2("Test2"); Console.WriteLine("new-way main thread invoker finished"); Console.ReadLine(); } } 

这两种方法做同样的事情,但是我似乎已经获得了(不需要EndInvoke和closures句柄,这是imho仍有点争议),我正在失去了新的方式,不得不等待一个Task.Yield()实际上提出了一个新的问题,就是不得不重写所有现有的asynchronousF&F方法来添加一行代码。 在性能/清理方面是否有一些无形的收益?

如果我不能修改背景方法,我将如何去应用asynchronous? 在我看来,没有直接的方式,我将不得不创build一个包装asynchronous方法,将等待Task.Run()?

编辑:我现在看到我可能会错过一个真正的问题。 问题是:给定一个同步方法A(),我怎么能asynchronous调用async / await ,而不是以一种难以忘怀的方式,而不是得到一个比“老方法”更复杂的解决scheme

避免async void 。 它有error handling周围棘手的语义; 我知道有些人称之为“火,忘了”,但我通常使用“火灾和崩溃”这个词。

问题是:给定一个同步方法A(),我怎么能asynchronous调用async / await,而不是以一种难以忘怀的方式,而不是得到一个比“老方法”更复杂的解决scheme

你不需要async / await 。 就像这样调用它:

 Task.Run(A); 

正如在其他答案中指出的,通过这个优秀的博客文章,你想避免在UI事件处理程序之外使用async void 。 如果你想要一个安全的 “消除和忘记” async方法,考虑使用这种模式(信贷给@ReedCopsey;这种方法是他聊天交谈给我的):

  1. Task创build一个扩展方法。 它运行通过的Task并捕获/logging任何exception:

     static async void FireAndForget(this Task task) { try { await task; } catch (Exception e) { // log errors } } 
  2. 在创build时总是使用Task风格的async方法,而不是async void

  3. 以这种方式调用这些方法:

     MyTaskAsyncMethod().FireAndForget(); 

你不需要await (也不会产生await警告)。 它也将正确地处理任何错误,因为这是唯一的地方你把async void ,你不必记得把try/catch块到处。

如果您真的想正常await ,也可以select使用async方法作为“fire and forget”方法。

对我来说,“等待”的东西似乎是“两个正交的概念”。 你可以asynchronous启动一个方法而不关心结果,或者你想在操作完成后(也可能使用返回值)在原始上下文中继续执行,这正是等待的。 如果你只是想在一个ThreadPool线程上执行一个方法(这样你的UI不会被阻塞),那就去吧

 Task.Factory.StartNew(() => DoIt2("Test2")) 

你会没事的

我的感觉是,这些“火又忘”的方法主要是需要一个干净的方式来交织UI和后台代码,以便你仍然可以编写你的逻辑作为一系列顺序指令的文物。 由于asynchronous/等待通过SynchronizationContext来处理编组,所以这变得不是一个问题。 内联代码中的一个更长的序列实际上将成为您以前在后台线程中从例程中启动的“即丢即忘”块。 这实际上是一种模式的倒置。

主要区别是await之间的块比BeginInvoke更类似于Invoke。 如果你需要更像BeginInvoke的行为,你可以调用下一个asynchronous方法(返回一个Task),然后不要等待返回的Task,直到你想要的代码BeginInvoke。

  public async void Method() { //Do UI stuff await SomeTaskAsync(); //Do more UI stuff (as if called via Invoke from a thread) var nextTask = NextTaskAsync(); //Do UI stuff while task is running (as if called via BeginInvoke from a thread) await nextTask; }