作爲i3arnon already answered,你可以用CancellationTokenSource.CreateLinkedTokenSource()
做到這一點。我想試圖展示一種如何使用這種標記的模式,以便在不取消整體任務的情況下區分取消總體任務與取消兒童任務。
async Task MyAsyncTask(
CancellationToken ct)
{
// Keep retrying until the master process is cancelled.
while (true)
{
// Ensure we cancel ourselves if the parent is cancelled.
ct.ThrowIfCancellationRequested();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
// Set a timeout because sometimes stuff gets stuck.
childCts.CancelAfter(TimeSpan.FromSeconds(32));
try
{
await DoSomethingAsync(childCts.Token);
}
// If our attempt timed out, catch so that our retry loop continues.
// Note: because the token is linked, the parent token may have been
// cancelled. We check this at the beginning of the while loop.
catch (OperationCancelledException) when (childCts.IsCancellationRequested)
{
}
}
}
當臨時令牌到期必須不會取消主令牌。
請注意,MyAsyncTask()
的簽名接受CancellationToken
而不是CancellationTokenSource
。由於該方法只能訪問CancellationToken
上的成員,因此它不會意外取消主/父令牌。我建議您以這樣的方式組織代碼,使主任務的CancellationTokenSource
儘可能少地顯示代碼。在大多數情況下,這可以通過將CancellationTokenSource.Token
傳遞給方法來完成,而不是共享對CancellationTokenSource
的引用。
我還沒有調查,但可能有反射的方式來強制取消CancellationToken
而無需訪問其CancellationTokenSource
。希望這是不可能的,但如果可能的話,這將被認爲是不好的做法,並不是普遍擔心的事情。
你是非常正確的。我開始使用CancellationTokenSource.CreateLinkedTokenSource,但認爲它不起作用。我忘記了當令牌超時時會拋出異常。這在我的代碼中被進一步捕獲。這給人的印象是沒有按我的預期工作。通過將我的調用放在try catch塊中,它工作正常。 – Retrocoder
@Retrocoder如果你只想捕獲內部的令牌,我會推薦使用像'try {doSomething(ct:childCts.Token); } catch(OperationCancelledException)when(childCts.IsCancellationRequested){}'。你可以把它放在重試循環中,並在循環內創建子令牌源。然後,當父令牌取消時,它會一直向上冒泡,但是當子令牌取消時,它只會重試。我無法從你的評論中得知 - 你可能已經正確地做到了這一點;-)。 – binki