简介
我有一个Windows服务,可以并行执行多个作业作为异步任务。然而,当调用OnStop时,似乎这些都被立即终止,而不是被允许以更加亲切的方式停止。
更详细
每个工作代表一次工作的迭代,因此完成工作后,工作需要再次运行。
为了实现这一目标,我正在编写一个概念验证Windows服务:
当我运行服务时,我看到所有内容都按预期执行。但是,当我停止服务时,似乎一切都停止了。
好的 - 这是怎么回事?
在服务中,我有一个取消令牌和一个TaskCompletion Source:
private static CancellationTokenSource _cancelSource = new CancellationTokenSource();
private TaskCompletionSource<bool> _jobCompletion = new TaskCompletionSource<bool>();
private Task<bool> AllJobsCompleted { get { return _finalItems.Task; } }
这个想法是,当每个Job都优雅地停止时,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);
}
Task 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);
}
}
我的期望是:
当我使用WPF Form应用程序调试时,我得到了这个。
但是,当我将其作为服务安装时:
我需要做些什么来确保OnStop不会终止我的并行异步作业并等待TaskCompletionSource?
答案 0 :(得分:11)
您的问题是OnStop
是async void
。因此,当它await this.AllJobsCompleted
时,实际发生的是从OnStop
返回,SCM将其解释为已停止,并终止该过程。
这是您需要阻止任务的罕见情况之一,因为在任务完成之前您不能允许OnStop
返回。
这应该这样做:
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);
}
}