Windows 服务 运行 异步代码未等待工作完成
Windows Service running Async code not waiting on work to complete
简述
我有一个 Windows 服务可以并行执行多个作业作为异步任务。然而,当调用 OnStop 时,似乎这些都立即终止,而不是被允许以更优雅的方式停止。
更详细
每个作业代表一个工作迭代,因此完成其工作后,作业需要再次 运行。
为此,我正在编写一个概念验证 Windows 服务:
- 运行 将每个作业作为等待的异步 TPL 任务(这些都是 I/O 绑定任务)
- 每个作业在一个循环中运行迭代
- 每个作业的循环是 运行 并行
当我 运行 服务时,我看到一切都按预期执行。但是,当我停止服务时,似乎一切都停止了。
好的 - 那么这是如何工作的?
在服务中我有一个取消令牌和一个任务完成源:
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);
}
}
我期待的是:
- 单击服务上的[停止]
- 服务应该需要一些时间才能停止
- 我应该看到跟踪语句 "Task cancellation requested."
- 我应该看到一条跟踪语句,上面写着 "allStopped = true" 或异常消息
当我使用 WPF Form 应用调试它时,我得到了这个。
但是,当我将它安装为服务时:
- 单击服务上的[停止]
- 服务几乎立即停止
- 我只看到跟踪语句"Task cancellation requested."
我需要做什么来确保 OnStop 不会终止我的并行异步作业并等待 TaskCompletionSource?
你的问题是 OnStop
是 async 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);
}
}
简述
我有一个 Windows 服务可以并行执行多个作业作为异步任务。然而,当调用 OnStop 时,似乎这些都立即终止,而不是被允许以更优雅的方式停止。
更详细
每个作业代表一个工作迭代,因此完成其工作后,作业需要再次 运行。
为此,我正在编写一个概念验证 Windows 服务:
- 运行 将每个作业作为等待的异步 TPL 任务(这些都是 I/O 绑定任务)
- 每个作业在一个循环中运行迭代
- 每个作业的循环是 运行 并行
当我 运行 服务时,我看到一切都按预期执行。但是,当我停止服务时,似乎一切都停止了。
好的 - 那么这是如何工作的?
在服务中我有一个取消令牌和一个任务完成源:
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);
}
}
我期待的是:
- 单击服务上的[停止]
- 服务应该需要一些时间才能停止
- 我应该看到跟踪语句 "Task cancellation requested."
- 我应该看到一条跟踪语句,上面写着 "allStopped = true" 或异常消息
当我使用 WPF Form 应用调试它时,我得到了这个。
但是,当我将它安装为服务时:
- 单击服务上的[停止]
- 服务几乎立即停止
- 我只看到跟踪语句"Task cancellation requested."
我需要做什么来确保 OnStop 不会终止我的并行异步作业并等待 TaskCompletionSource?
你的问题是 OnStop
是 async 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);
}
}