图案自我消除和重新启动任务 [英] A pattern for self-cancelling and restarting task
问题描述
有没有推荐的既定模式自我消除和重新启动任务?
Is there a recommended established pattern for self-cancelling and restarting tasks?
例如,我正在后台拼写检查程序的API。拼写检查会话被包装为工作
。每一个新的会话应该取消previous之一,并等待其结束(正确地重复使用资源,如拼写检查服务提供商等)。
E.g., I'm working on the API for background spellchecker. The spellcheck session is wrapped as Task
. Every new session should cancel the previous one and wait for its termination (to properly re-use the resources like spellcheck service provider, etc).
我已经出来是这样的:
class Spellchecker
{
Task pendingTask = null; // pending session
CancellationTokenSource cts = null; // CTS for pending session
// SpellcheckAsync is called by the client app
public async Task<bool> SpellcheckAsync(CancellationToken token)
{
// SpellcheckAsync can be re-entered
var previousCts = this.cts;
var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
this.cts = newCts;
if (IsPendingSession())
{
// cancel the previous session and wait for its termination
if (!previousCts.IsCancellationRequested)
previousCts.Cancel();
// this is not expected to throw
// as the task is wrapped with ContinueWith
await this.pendingTask;
}
newCts.Token.ThrowIfCancellationRequested();
var newTask = SpellcheckAsyncHelper(newCts.Token);
this.pendingTask = newTask.ContinueWith((t) => {
this.pendingTask = null;
// we don't need to know the result here, just log the status
Debug.Print(((object)t.Exception ?? (object)t.Status).ToString());
}, TaskContinuationOptions.ExecuteSynchronously);
return await newTask;
}
// the actual task logic
async Task<bool> SpellcheckAsyncHelper(CancellationToken token)
{
// do not start a new session if the the previous one still pending
if (IsPendingSession())
throw new ApplicationException("Cancel the previous session first.");
// do the work (pretty much IO-bound)
try
{
bool doMore = true;
while (doMore)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500); // placeholder to call the provider
}
return doMore;
}
finally
{
// clean-up the resources
}
}
public bool IsPendingSession()
{
return this.pendingTask != null &&
!this.pendingTask.IsCompleted &&
!this.pendingTask.IsCanceled &&
!this.pendingTask.IsFaulted;
}
}
客户端应用程序(用户界面)应该只能够调用 SpellcheckAsync
作为根据需要,而不必担心取消一个未决的会话多次。主要的 doMore
循环在UI线程上运行(因为它涉及到用户界面,而所有拼写检查服务提供商调用是IO绑定)。
The client app (the UI) should just be able to call SpellcheckAsync
as many times as desired, without worrying about cancelling a pending session. The main doMore
loop runs on the UI thread (as it involves the UI, while all spellcheck service provider calls are IO-bound).
我觉得有点不舒服的事实,我不得不将API分成两个peices, SpellcheckAsync
和 SpellcheckAsyncHelper
,但我想不出这样做的更好的办法,这是有待考验。
I feel a bit uncomfortable about the fact that I had to split the API into two peices, SpellcheckAsync
and SpellcheckAsyncHelper
, but I can't think of a better way of doing this, and it's yet to be tested.
推荐答案
我觉得一般的概念是pretty的不错,但我建议你不要使用 ContinueWith
。
I think the general concept is pretty good, though I recommend you not use ContinueWith
.
使用我只是写正规等待
,以及大量的我我已经运行的逻辑是没有必要的:
I'd just write it using regular await
, and a lot of the "am I already running" logic is not necessary:
Task pendingTask = null; // pending session
CancellationTokenSource cts = null; // CTS for pending session
// SpellcheckAsync is called by the client app on the UI thread
public async Task<bool> SpellcheckAsync(CancellationToken token)
{
// SpellcheckAsync can be re-entered
var previousCts = this.cts;
var newCts = CancellationTokenSource.CreateLinkedTokenSource(token);
this.cts = newCts;
if (previousCts != null)
{
// cancel the previous session and wait for its termination
previousCts.Cancel();
try { await this.pendingTask; } catch { }
}
newCts.Token.ThrowIfCancellationRequested();
this.pendingTask = SpellcheckAsyncHelper(newCts.Token);
return await this.pendingTask;
}
// the actual task logic
async Task<bool> SpellcheckAsyncHelper(CancellationToken token)
{
// do the work (pretty much IO-bound)
using (...)
{
bool doMore = true;
while (doMore)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500); // placeholder to call the provider
}
return doMore;
}
}
这篇关于图案自我消除和重新启动任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!