是否可以将异步事件处理程序附加到System.Timers.Timer?

时间:2016-08-31 23:22:25

标签: c# timer async-await

我已阅读过帖子here和文章here 我有一个计时器事件,每隔一段时间触发一次,我想在处理程序中进行一些异步处理,所以有类似的东西:

        Timer timer = new Timer();
        timer.Interval = 1000;
        timer.Elapsed += timer_Elapsed; // Please ignore this line. But some answers already given based on this line so I will leave it as it is.
        timer.Elapsed += async (sender, arguments) => await timer_Elapsed(sender, arguments);


       private async Task timer_Elapsed(object sender, ElapsedEventArgs e)
       {
          await Task.Delay(10);
       }

上面的代码编译和工作。

但我不确定为什么代码正在编译。 ElapsedEventHandler预期签名是

      void timer_Elapsed(object sender, ElapsedEventArgs e)

但是我的方法会返回Task而不是void,因为不推荐使用async void。但这与ElapsedEventHandler签名不匹配,而且仍在编辑&工作

可以在Timer.Elapsed上调用asyn方法吗?代码将在windows服务中执行。

更新1

  

async void是"不推荐",有一个非常重要的例外:   事件处理程序。

它的异步事件处理程序或同步事件处理程序是否重要?

MSDN文章here csays

  

Void-returns async方法有一个特定的目的:make   异步事件处理程序可能。

Timer.Elapsed我认为同步事件处理程序是否仍然可以附加async void

2 个答案:

答案 0 :(得分:7)

async void是"不推荐",有一个非常重要的例外:事件处理程序。

您的代码编译得很好(第二个事件订阅......第一个会产生编译时错误,假设两个语句中都使用相同的timer_Elapsed()方法),因为编译器可以推断出委托代码返回类型应为voidasync匿名方法可以也返回Task,但在这种情况下,这将是错误的方法签名,因此您将获得void

将事件句柄声明为async void

也可以
   private async void timer_Elapsed(object sender, ElapsedEventArgs e)
   {
      await Task.Delay(10);
   }

用过:

timer.Elapsed += timer_Elapsed;

void方法返回async并不理想,但对于事件处理程序,无论如何都没有代码会使用Task(除非事件是专门用于理解async方法,如Asynchronous events in C#)。如果你从中获得零利益,那么没有理由向后弯腰以遵守原本正确的编码实践。


另请参阅Should I avoid 'async void' event handlers?


附录:

从编辑到问题:

  

Timer.Elapsed我认为同步事件处理程序是否仍然可以附加async void

不是异步或同步的事件,而是处理程序本身。这完全取决于您是否使用asyncawait作为处理程序方法。正如您的问题和我的回答所述,您可以使用async void处理程序方法和Elapsed事件,就像您可以使用任何其他事件一样(假设事件签名需要void as处理程序返回类型,这当然是传统.NET事件的标准。)

答案 1 :(得分:0)

你是否正确避免“异步无效”。不确定编译器没有反对,但你订阅了同一个事件两次。

您应该将异步部分保留在处理程序中,而不是调用处理程序。

尝试删除重复的事件订阅:

timer.Elapsed += async (sender, arguments) => await timer_Elapsed(sender, arguments);

并从以下位置更改处理程序的签名:

private async Task timer_Elapsed(object sender, ElapsedEventArgs e)

为:

private void timer_Elapsed(object sender, ElapsedEventArgs e)

然后在处理程序中执行异步部分,使用类似本文中的建议: https://stackoverflow.com/a/31469699/6776481

“WrapSomeMethod”调用只会返回Task,而不是Task<string>

此外,由于这是在Windows服务内部,请不要忘记在必要时取消订阅。