是否存在推薦建立的自我取消和重新啓動任務的模式?自我取消和重新啓動任務的模式
例如,我正在開發背景拼寫檢查程序的API。拼寫檢查會議包裝爲Task
。每個新會話都應該取消前一個會話並等待終止(正確地重新使用拼寫檢查服務提供程序等資源)。
我想出這樣的事情:
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線程上運行(因爲它涉及UI,而所有拼寫檢查服務提供者調用都是IO綁定的)。
我覺得有點不舒服,因爲我不得不將API分成兩個版本,分別是SpellcheckAsync
和,但我想不出一個更好的方法來做到這一點,它還有待測試。
@Stephen Cleary,我非常尊重你在所有異步事物上的工作,所以請不要誤以爲這是我的好奇心。我有些驚訝,你沒有用'SemaphoreSlim'或你自己的'AsyncLock'或類似的東西重寫'await this.pendingTask'部分。您是否一般認爲提高異步方法的「同步」部分中的線程安全性是一個過早的優化? –
@KirillShlenskiy:使用「SemaphoreSlim」或其他類似的東西來限制每次一次的限制沒有任何問題。 –