Windows 服务 运行 异步代码未等待工作完成

Windows Service running Async code not waiting on work to complete

简述

我有一个 Windows 服务可以并行执行多个作业作为异步任务。然而,当调用 OnStop 时,似乎这些都立即终止,而不是被允许以更优雅的方式停止。

更详细

每个作业代表一个工作迭代,因此完成其工作后,作业需要再次 运行。

为此,我正在编写一个概念验证 Windows 服务:

当我 运行 服务时,我看到一切都按预期执行。但是,当我停止服务时,似乎一切都停止了。

好的 - 那么这是如何工作的?

在服务中我有一个取消令牌和一个任务完成源:

private static CancellationTokenSource _cancelSource = new CancellationTokenSource();
private TaskCompletionSource<bool> _jobCompletion = new TaskCompletionSource<bool>();
private Task<bool> AllJobsCompleted { get { return _finalItems.Task; } }

想法是,当每个作业正常停止时,任务 AllJobsCompleted 将被标记为已完成。

OnStart 只是启动 运行 宁这些工作:

protected override async void OnStart(string[] args)
{
    _cancelSource = new CancellationTokenSource();  
    var jobsToRun = GetJobsToRun(); // details of jobs not relevant 
    Task.Run(() => this.RunJobs(jobsToRun, _cancelSource.Token).ConfigureAwait(false), _cancelSource.Token);
}

任务 RunJobs 将 运行 并行循环中的每个作业:

private async Task RunModules(IEnumerable<Jobs> jobs, CancellationToken cancellationToken)
{
    var parallelOptions = new ParallelOptions { CancellationToken = cancellationToken };    
    int jobsRunningCount = jobs.Count();
    object lockObject = new object();

    Parallel.ForEach(jobs, parallelOptions, async (job, loopState) =>
    {
        try
        {
            do
            {
                await job.DoWork().ConfigureAwait(false); // could take 5 seconds
                parallelOptions.CancellationToken.ThrowIfCancellationRequested();
            }while(true);
        }
        catch(OperationCanceledException)
        {
            lock (lockObject) { jobsRunningCount --; }
        }
    }); 

    do
    {
        await Task.Delay(TimeSpan.FromSeconds(5));
    } while (modulesRunningCount > 0);

    _jobCompletion.SetResult(true);
}

所以,应该发生的是,当每个作业完成其当前迭​​代时,它应该看到已发出取消信号,然后它应该退出循环并递减计数器。

然后,当 jobsRunningCount 达到零时,我们更新 TaskCompletionSource。 (可能有更优雅的方法来实现这一点...)

因此,对于 OnStop:

protected override async void OnStop()
{
    this.RequestAdditionalTime(100000); // some large number        
    _cancelSource.Cancel();     
    TraceMessage("Task cancellation requested."); // Last thing traced

    try
    {
        bool allStopped = await this.AllJobsCompleted;          
        TraceMessage(string.Format("allStopped = '{0}'.", allStopped));
    }
    catch (Exception e)
    {
        TraceMessage(e.Message);
    }
} 

我期待的是:

  1. 单击服务上的[停止]
  2. 服务应该需要一些时间才能停止
  3. 我应该看到跟踪语句 "Task cancellation requested."
  4. 我应该看到一条跟踪语句,上面写着 "allStopped = true" 或异常消息

当我使用 WPF Form 应用调试它时,我得到了这个。

但是,当我将它安装为服务时:

  1. 单击服务上的[停止]
  2. 服务几乎立即停止
  3. 我只看到跟踪语句"Task cancellation requested."

我需要做什么来确保 OnStop 不会终止我的并行异步作业并等待 TaskCompletionSource?

你的问题是 OnStopasync void。因此,当它执行 await this.AllJobsCompleted 时,实际发生的是它 returns 来自 OnStop,SCM 将其解释为已停止,并终止过程。

这是您需要阻止任务的罕见情况之一,因为在任务完成之前您不能允许 OnStop 到 return。

应该这样做:

protected override void OnStop()
{
  this.RequestAdditionalTime(100000); // some large number        
  _cancelSource.Cancel();     
  TraceMessage("Task cancellation requested."); // Last thing traced

  try
  {
    bool allStopped = this.AllJobsCompleted.GetAwaiter().GetResult();          
    TraceMessage(string.Format("allStopped = '{0}'.", allStopped));
  }
  catch (Exception e)
  {
    TraceMessage(e.Message);
  }
}