模式与能力取消长时间运行的操作 [英] Pattern for long-running operation with cancellation ability
问题描述
为了进行长时间运行(让它在这种情况下搜索)操作,我把装载逻辑的TPL任务里面,所以一般的方法搜索()被称为后台线程。 搜索()操作可够长的了,所以我需要正确地取消它的能力,使用的的CancellationToken 。但搜索()操作没有返回,直到完成它,所以我必须做一些逻辑,以实现方便和(!)快速取消。
In order to perform long-running (let it be search in this context) operation, I put the loading logic inside a TPL task, so the general method Search() is called on background thread. Search() operation can be long enough, so I need the ability to cancel it properly, using the CancellationToken. But the Search() operation did not return until it finished, so I have to do some logic in order to implement convenient and (!) fast cancellation.
使用的的的WaitHandle的 我可以实现这样的:
Using WaitHandle's I can implement something like this:
private void StartSearch() // UI thread
{
CancellationTokenSource s = new CancellationTokenSource();
Task.Factory.StartNew(() => StartSearchInternal(s.Token), s.Token)
}
private void StartSearchInternal(CancellationToken token) // Main Background Thread
{
ManualResetEvent eHandle = new ManualResetEvent(false);
Task.Factory.StartNew(() => Search(eHandle ), TaskScheduler.Default);
WaitHandle.WaitAny(new [] { eHandle, token.WaitHandle });
token.ThrowIfCancellationRequested();
}
private IEnumerable<object> Search(ManualResetEvent e) // Another Background thread
{
try
{
// Real search call, i.e. to database, service, or AD, doesn't matter
return RealSearch();
}
catch {} // Here, for simplicity of question, catch and eat all exceptions
finally
{
try
{
e.Set();
}
catch {}
}
}
这是在我看来,事实并非如此优雅的解决方案,可以作出
It's seems to me that this is not so elegant solution, that can be made.
问:是否有此任务的任何其他方法?
Q: Is there any other approaches for this task?
推荐答案
这是我的意见重构到包含代码的答案。它包含几个选择使用Task.Wait和异步模式,其选择将取决于你是否调用从UI线程的方法。
This is my comment refactored into an answer containing code. It contains a couple of alternatives for using Task.Wait and the async pattern, the choice of which will depend on whether you call the method from the UI thread.
有一些意见的O / P和包含有关异步行为的有价值的信息,其他的答案。 。请仔细阅读这些如下面的代码有很多改进的机会
There are several comments to the O/P and other answers that contain valuable information regarding asynchronous behaviours. Please read these as the code below has many 'opportunities for improvement'.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace SearchAgent
{
class CancellableSearchAgent
{
// Note: using 'object' is a bit icky - it would be better to define an interface or base class,
// or at least restrict the type of object in some way, such as by making CancellableSearchAgent
// a template CancellableSearchAgent<T> and replacing every copy of the text 'object' in this
// answer with the character 'T', then make sure that the RealSearch() method return a collection
// of objects of type T.
private Task<IEnumerable<object>> _searchTask;
private CancellationTokenSource _tokenSource;
// You can use this property to check how the search is going.
public TaskStatus SearchState
{
get { return _searchTask.Status; }
}
// When the search has run to completion, this will contain the result,
// otherwise it will be null.
public IEnumerable<object> SearchResult { get; private set; }
// Create a new CancellableSearchAgent for each search. The class encapsulates the 'workflow'
// preventing issues with null members, re-using completed tasks, etc, etc.
// You can add parameters, such as SQL statements as necessary.
public CancellableSearchAgent()
{
_tokenSource = new CancellationTokenSource();
_searchTask = Task<IEnumerable<object>>.Factory.StartNew(() => RealSearch(), TaskScheduler.Default);
}
// This method can be called from the UI without blocking.
// Use this if the CancellableSearchAgent is part of your ViewModel (Presenter/Controller).
public async void AwaitResultAsync()
{
SearchResult = await _searchTask;
}
// This method can be called from the ViewModel (Presenter/Controller), but will block the UI thread
// if called directly from the View, making the UI unresponsive and unavailable for the user to
// cancel the search.
// Use this if CancellableSearchAgent is part of your Model.
public IEnumerable<object> AwaitResult()
{
if (null == SearchResult)
{
try
{
_searchTask.Wait(_tokenSource.Token);
SearchResult = _searchTask.Result;
}
catch (OperationCanceledException) { }
catch (AggregateException)
{
// You may want to handle other exceptions, thrown by the RealSearch() method here.
// You'll find them in the InnerException property.
throw;
}
}
return SearchResult;
}
// This method can be called to cancel an ongoing search.
public void CancelSearch()
{
_tokenSource.Cancel();
}
}
}
这篇关于模式与能力取消长时间运行的操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!