后台任务 - 如何以受控方式停止?

时间:2013-10-03 08:33:32

标签: c# multithreading

我正在开发一个WPF应用程序,它将作为后台任务运行“索引服务”。索引服务将使用监视文件夹的FileSystemWatcher - 当文件更改时,索引服务将读入文件内容并更新索引(我使用的是Lucene.Net)。我的索引服务是一个单例,将在应用程序启动期间启动,如下所示: -

new TaskFactory().StartNew(_indexingService.StartService);

StartService()方法如下所示: -

private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);

public void StartService()
{
    var watcher = new FileSystemWatcher
    {
        // Set the properties
    };
    watcher.Changed += UpdateIndexes();

    _resetEvent.WaitOne();
}

当应用程序关闭时,我打算调用此方法,据我所知,它将结束索引服务后台任务: -

public void StopService()
{
    _resetEvent.Set();
}

首先,这是启动和停止应该在应用程序生命周期内运行的后台任务的正确“模式”吗?

其次,这种关闭会有多“优雅”?假设观察者Changed事件处理程序已经触发并正在迭代文件,读取它们并更新索引。如果任务已停止,是否会在流程中中止此处理,或者事件处理程序方法是否会先运行完成?

3 个答案:

答案 0 :(得分:3)

您可以使用取消令牌:

  CancellationTokenSource CancelationToken = new CancellationTokenSource();
  new TaskFactory().StartNew(_indexingService.StartService,CancelationToken,
                              TaskCreationOptions.LongRunning)   
.ContinueWith(TaskCancelationCallBack,TaskContinuationOptions.OnlyOnCanceled);   

您可以使用以下方式在应用程序中的任何位置取消令牌:

 CancellationTokenSource.Cancel();

您可以检查您的令牌是否被取消,并从内部抛出取消异常任务:

if (CancelationToken.IsCancellationRequested) {    

    CancelationToken.Token.ThrowIfCancellationRequested(); 
}         

您可以在ContinueWith回调中获取任务状态:

private void TaskCancelationCallBack(System.Threading.Tasks.Task task)
{
  if (task.Status == System.Threading.Tasks.TaskStatus.Canceled)
  {
         //Canceled
  } 
}

编辑:在这种情况下,我们使用了TaskContinuationOptions.OnlyOnCanceled,因此无需检查TaskCancelationCallBack。它只会在这个前提下解雇。

答案 1 :(得分:1)

@Carlos Landeras

ThrowIfCancellationRequested检查是否请求取消,因此嵌入检查相同的if语句是多余的。

http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.throwifcancellationrequested.aspx

This method provides functionality equivalent to:
    if (token.IsCancellationRequested) 
        throw new OperationCanceledException(token);

答案 2 :(得分:0)

如果你取消了对waitOne的调用,那么任务就会结束​​。

要让后台任务真正做某事,在某些时候需要有一个循环来处理,比如

void ProcessItems()
{
    while(workItems.Count > 0)
    {
        ProcessItem(workItems[0]);
    }
}

如果你想优雅地拯救,你可以做两件事。在我的例子中,我会有一面旗帜。

bool m_IsRunning = true;
public void Stop()
{
    m_IsRunning = false;
}
void ProcessItems()
{
    while(workItems.Count > 0 && m_IsRunning)
    {
            ProcessItem(workItems[0]);
    }
}

使用任务并行库,您还可以传入CancellationToken

  

在任务类中,取消涉及到之间的合作   user delegate,表示可取消的操作和代码   要求取消。取消成功涉及到   请求代码调用CancellationTokenSource.Cancel方法,   并且用户委托及时终止操作。   您可以使用以下选项之一终止操作:

     
      
  • 只需从代表返回即可。在许多情况下,这已足够;但是,以这种方式“取消”的任务实例
      转换到RanToCompletion状态,而不是转换为Canceled状态。

  •   
  • 通过抛出OperationCanceledException并将请求取消的令牌传递给它。这样做的首选方法是
      使用ThrowIfCancellationRequested方法。一项任务是   以这种方式取消转换到已取消状态,其中   调用代码可用于验证任务是否响应其中   取消请求。

  •   

http://msdn.microsoft.com/en-us/library/dd997396.aspx