如何处理TopShelf中的异步Start()错误

Hyd*_*rum 8 c# asynchronous topshelf async-await

我有一个TopShelf服务,它使用异步代码连接到Web服务和其他应用程序服务器.

如果它无法在启动时初始化其连接,则服务应记录一些错误并正常停止.

我已经看过这个关于在不满足启动条件时停止TopShelf的问题.这个答案谈到使用TopShelf HostControl来停止服务.

但是,答案依赖于该ServiceConfigurator<T>.WhenStarted<T>(Func<T, HostControl, bool> start)方法.

我目前正在以标准方式配置TopShelf服务:

x.Service<MyService>(s =>
{
    s.ConstructUsing(() => new MyService());
    s.WhenStarted(s => s.Start());
    s.WhenStopped(s => s.Stop());
});
Run Code Online (Sandbox Code Playgroud)

但是我的服务Start()方法实际上async是这样定义的:

public async void Start()
{
    await Init();
    while (!_canceller.Token.IsCancellationRequested)
    {
        await Poll();
    }
}
Run Code Online (Sandbox Code Playgroud)

这似乎工作正常.但是我在函数的几个地方使用了await关键字.所以,我不能简单地改变我的Start()方法来取一个HostControl并返回一个bool,因为我必须Task<bool>从一个async方法返回.

我目前正在允许异常从Start()函数冒出来,以便TopShelf可以看到它们并在异常冒泡时自动停止服务.但是,我的代码完全没有处理异常,因此我在写入的各种日志中最终出现了令人讨厌的未处理异常错误消息.我希望用一个很好的错误消息和一个干净的服务关闭来替换.

所以,我有两个问题:

  1. 使用async void Start()TopShelf 的方法有什么问题吗?
  2. 有没有办法让它如此Init()抛出异常,优雅地记录异常细节,然后服务停止,因为我的服务运行async代码?

sel*_*ape 13

首先,async void几乎总是不正确的,除了在一些真正的"一劳永逸"的场景中.你想改变它async Task.

然后有时你只需要.Wait()在同步和异步代码之间的边界使用.在这种情况下,您可能希望将当前的异步Start()方法重命名为StartAsync()并添加一个Start()调用它的方法:

public void Start()
{
    StartAsync().Wait();
}

public async Task StartAsync()
{
    await Init();
    while (!_canceller.Token.IsCancellationRequested)
    {
        await Poll();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,你有另一个问题,因为TopShelf的Start()方法不是一个"Run"()方法; 即,您应该在服务启动后立即从该方法返回,而不是在服务运行时保留在那里.你已经在使用异步的await考虑,我可能反而不叫Wait()Start(),但保存Task的返回StartAsync(),那么当Stop()被调用,显示你Task停止使用现有的_canceller,并且只有在Stop()通话.Wait(),让你有这样的事情:

private Task _serviceTask;

public void Start()
{
    Init().Wait();
    _serviceTask = ExecuteAsync();
}

public void Stop()
{
    _canceller.Cancel();
    _serviceTask.Wait();
}

public async Task ExecuteAsync()
{
    while (!_canceller.Token.IsCancellationRequested)
    {
        await Poll();
    }
}
Run Code Online (Sandbox Code Playgroud)

我应该添加你的方式,你可能Start()会把事情放到一定程度,因为你的异步方法会在它碰到第一个时返回TopShelf await,但会继续执行.如果您的Stop()方法调用,_canceller.Cancel()那么您的异步Start()方法将在下次调用时终止Poll().

然而,上面的内容更清晰,你必须能够等到最后一次Poll()完成执行,这是你之前没有的.正如您所提到的,您还可以处理异常.

编辑 我也会将Init()呼叫转移到Start(),如上所述.