具有取消能力的长时间运行模式
为了执行长时间运行(让它在此上下文中搜索)操作,我将加载逻辑放在TPL任务中,因此在后台线程上调用通用方法Search() 。 Search()操作可以足够长,因此我需要能够使用CancellationToken正确取消它。 但是Search()操作直到完成才返回,所以我必须做一些逻辑才能实现方便和(!)快速取消。
使用WaitHandle我可以实现这样的事情:
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 Search(ManualResetEvent e) // Another Background thread { try { // Real search call, ie 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 {} } }
在我看来,这不是那么优雅的解决方案,可以制作。
问:这项任务还有其他方法吗?
这是我的评论重构为包含代码的答案。 它包含几个使用Task.Wait和异步模式的替代方案,其选择取决于您是否从UI线程调用该方法。
对O / P和其他答案有几点评论,其中包含有关异步行为的有价值信息。 请阅读这些,因为下面的代码有很多“改进的机会”。
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 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> _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
如果您可以控制StartSearchInternal()
和Search(eHandle)
,那么您应该可以在Search
Core循环中使用ThrowIfCancellationRequested
进行协作取消。
有关更多详细信息,我强烈建议您阅读本文档: “在.NET Framework 4中使用取消支持” 。
另外,您应该在ViewModel类的某个位置存储对Task.Factory.StartNew(() => StartSearchInternal(s.Token), s.Token)
返回的任务的引用。 您很可能希望观察其结果以及可能抛出的任何exception。 您可能想要检查Lucian Wischik的“Async re-entrancy,以及处理它的模式” 。