我正在处理一个Windows服务的错误。该服务使用一个字段来跟踪对单个Task的引用。在启动时,任务执行单个方法。该方法内部有一个循环,并以可配置的间隔调用数据库,以监视另一个系统正在执行的工作。
protected override void OnStart(string[] args)
{
_processorTask = Task.Run(() => StartProcessor());
}
我们偶尔会遇到这样的问题,Task会终止并记录异常,但现有的管道并没有告诉服务停止,所以我们的服务监视器不知道有什么问题。
起初,我尝试添加一个对Stop((的调用。
private void StartProcessor()
{
var processor = new PEMonitoringProcessor(_tokenSource.Token);
try
{
// The process loop is in the function. If this method exits, good or bad, the service should stop.
processor.ProcessRun();
}
catch (Exception ex)
{
// An exception caught here is most likely fatal. Log the ex and start the service shutdown.
if (log.IsFatalEnabled) { log.Fatal("A fatal error has occurred.", ex); };
}
finally
{
Stop();
}
}
然而,我的一位开发伙伴在OnStop方法中注意到,使用一个令牌来通知Task停止,然后等待。如果Task调用Stop并等待Stop返回,而OnStop正在等待Task结束,那么这对代码来说不是好兆头。
protected override void OnStop()
{
_tokenSource.Cancel();
try
{
_processorTask.Wait();
}
// logging & clean-up...
}
我考虑了一个单独的Task,它不会被OnStop等待,它会检查第一个Task的状态,并在第一个Task完成、出错等时调用Stop,但这似乎有点奇怪。我还考虑过引发一个事件并尝试类似BeginInvoke的方法。
有意停止服务运行良好,因为OnStop通过Token发出关闭信号。我试图掩盖Task方法意外返回或抛出的可能性,我希望服务停止,而不是变成僵尸。
我看到的最直接的方法是这样的:
protected override void OnStart(string[] args) {
_processorTask = Task.Run(() => StartProcessor());
_processorTask.ContinueWith(x => {
// x.Exception contains exception if any, maybe log it here
Stop();
}, TaskContinuationOptions.NotOnCanceled);
}
protected override void OnStop() {
//or !_processorTask.IsCompleted && !_processorTask.IsCanceled && !_processorTask.IsFaulted
if (_processorTask.Status == TaskStatus.Running) {
// only cancel and wait if still running. Won't be the case if service is stopping from ContinueWith above
_tokenSource.Cancel();
_processorTask.Wait();
}
}
另一种方法:
protected override async void OnStart(string[] args) {
_processorTask = Task.Run(() => StartProcessor());
bool cancelled = false;
try {
await _processorTask;
}
catch (OperationCanceledException) {
// cancelled
cancelled = true;
}
catch (Exception ex) {
// log it?
}
if (!cancelled)
Stop();
}
// OnStop stays the same