4

我正在开发一个 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事件处理程序已触发并正在遍历文件,读取它们并更新索引。如果任务停止,这个处理会在流程中中止,还是事件处理程序方法会首先运行到完成?

4

3 回答 3

4

您可以使用取消令牌:

  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 中进行检查。它只会在那个前提下被解雇。

于 2013-10-03T08:37:50.230 回答
1

@卡洛斯兰德拉斯

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);
于 2014-02-11T10:10:43.227 回答
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.

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

  • 通过简单地从代表返回。在许多情况下,这已经足够了;但是,以这种方式“取消”的任务实例会
    转换到 RanToCompletion 状态,而不是 Canceled 状态。

  • 通过抛出 OperationCanceledException 并将请求取消的令牌传递给它。执行此操作的首选方法是
    使用 ThrowIfCancellationRequested 方法。以这种方式取消的任务将
    转换到 Canceled 状态,
    调用代码可以使用该状态来验证任务是否响应了它的
    取消请求。

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

于 2013-10-03T08:46:27.513 回答