在 C# Windows 服务中使用异步事件处理程序



我有一个C#(4.8(Windows服务。它所做的只是 OnStart 它注册一些事件处理程序,当消息到达时,消息队列会调用这些处理程序,事件处理程序来处理它。事件处理程序调用库方法来处理它。这都是同步的。现在,作为使所有内容异步等待模式的一部分,库中的所有处理逻辑现在都重写为异步等待。现在的问题是 Windows 服务中的事件处理程序。当我使它们异步无效时,我收到错误,说它需要是一项任务。我知道在UI应用程序中我们可以使异步无效。在Windows服务中不允许这样做吗?还说我让它异步无效,那么我有处理异常的问题,因为它们不会被捕获,因为没有任务。我还需要考虑 w.r.t 同步上下文是否有任何影响,因为 Win 服务无论如何都具有默认上下文,并且事件处理程序在异步时捕获上下文。

如果要使用 C# 事件处理程序,那么您将坚持使用void.这是没有办法的。如果您想在事件处理程序中使用async代码,那么您将坚持使用async void.

这并不一定是坏事。事件实际上只是"嘿,这发生了"的通知,然后继续前进。引发事件的代码的工作不是关注事件处理程序执行的操作或何时或是否成功完成。它的工作是说"嘿,这发生了"。

反正大多数时候....这种关注点的分离有时会变得模糊。例如,以 WebForms 中的Form.Closing事件为例。这是窗口即将关闭的通知。事件处理程序返回后,窗口将关闭。但是await通过稍后返回并完成其余工作来起作用。这意味着异步Closing事件处理程序在窗口关闭之前不会完全完成,这当然会导致问题。

因此,如果:

  • 您的代码不依赖于事件处理程序在执行其他工作之前成功完成,
  • 您的代码不会处理或关心事件处理程序代码中的异常,或者
  • 您希望允许多个事件处理程序订阅

然后只使用普通的事件处理程序。

另一种方法是接受异步委托作为初始化方法之一的参数(如果需要,甚至可以接受属性(。例如,考虑以下类:

public class Test {
private readonly Func<Task> _somethingHappenedHandler;

public Test(Func<Task> somethingHappenedHandler) {
_somethingHappenedHandler = somethingHappenedHandler;
}

public async Task DoSomething() {
//do something
await _somethingHappenedHandler();
}
}

你可以像这样使用:

public async Task SomethingHappened() {
await Task.Delay(1000);
Console.WriteLine("Something happened");
}
var test = new Test(SomethingHappened);
await test.DoSomething();

如果需要知道事件处理程序在继续之前已成功完成,则可以使用此方法。但是您失去了订阅多个事件处理程序的能力,但这可能不是您关心的事情。

您还应该考虑如何处理处理程序中发生的异常。你会忽略它们并将负担放在事件处理程序代码上吗?还是以某种方式处理它们?

您可以执行async void但它将作为即发即弃事件执行 - 事件发送者无法等待它,因此它无法知道您何时完成事件处理。如果您在用例中对此行为感到满意,只需在处理程序中添加 try catch(如您所说,无法处理async void方法引发的错误。如果必须在将控制权返回给发件人之前完成处理 - 请在异步处理程序上使用Wait()。据我所知,Windows服务没有同步上下文,您不会在这里遇到死锁。

最新更新