运行异步代码的Windows服务不等待工作完成 [英] Windows Service running Async code not waiting on work to complete

查看:126
本文介绍了运行异步代码的Windows服务不等待工作完成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简介

我有一个Windows服务,它可以并行执行多个作业作为异步任务.但是,在调用OnStop时,似乎所有这些都立即终止,而不是被允许以更亲切的方式停止.

I have a Windows Service that executes several jobs as async Tasks in parallel. However, when the OnStop is called, it seems that these are all immediately terminated instead of being allowed to stop in a more gracious manner.

更多详情

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

Each job represents an iteration of work, so having completed its work the job then needs to run again.

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

To accomplish this, I am writing a proof-of-concept Windows Service that:

  • 将每个作业作为等待的异步TPL任务运行(这些都是I/O绑定的任务)
  • 每个作业在一个循环中迭代运行
  • 每个作业的循环并行运行

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

When I run the Service, I see everything executing as I expect. However, when I Stop the service, it seems that everything stops dead.

好的-那么这是怎么工作的?

Okay - so how is this working?

在服务中,我有一个取消令牌和一个TaskCompletion源:

In the Service I have a cancellation token, and a TaskCompletion Source:

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

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

The idea is that when every Job has gracefully stopped, then the Task AllJobsCompleted will be marked as completed.

OnStart只是开始运行以下作业:

The OnStart simply starts running these jobs:

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);
}

Task RunJobs将在并行循环中运行每个作业:

The Task RunJobs will run each job in a parallel loop:

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);
}

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

So, what should be happening is that when each job finishes its current iteration, it should see that the cancellation has been signalled and it should then exit the loop and decrement the counter.

然后,当jobsRunningCount达到零时,我们将更新TaskCompletionSource. (实现这一目标可能会有更优雅的方式...)

Then, when jobsRunningCount reaches zero, then we update the TaskCompletionSource. (There may be a more elegant way of achieving this...)

因此,对于OnStop:

So, for the 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. 我应该看到一条跟踪语句请求取消任务".
  4. 我应该看到一条跟踪语句,说"allStopped = true"或异常消息

当我使用WPF表单应用程序调试时,我得到了.

And when I debug this using a WPF Form app, I get this.

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

However, when I install it as a service:

  1. 在服务上单击[停止]
  2. 服务几乎立即停止
  3. 我只看到跟踪语句请求取消任务".

我需要做什么以确保OnStop不会杀死我的并行异步作业并等待TaskCompletionSource?

What do I need to do to ensure the OnStop doesn't kill off my parallel async jobs and waits for the TaskCompletionSource?

推荐答案

您的问题是OnStopasync void.因此,当它执行await this.AllJobsCompleted时,实际上发生的是它从OnStop返回 (SCM将其解释为已停止)并终止该过程.

Your problem is that OnStop is async void. So, when it does await this.AllJobsCompleted, what actually happens is that it returns from OnStop, which the SCM interprets as having stopped, and terminates the process.

这是您需要阻止任务的罕见情况之一,因为您必须等到任务完成后才能允许OnStop返回.

This is one of the rare scenarios where you'd need to block on a task, because you cannot allow OnStop to return until after the task completes.

这应该做到:

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);
  }
}

这篇关于运行异步代码的Windows服务不等待工作完成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆