這是一個很簡單的非同步程式碼(.NET 8, C# 12):
可以使用 CancellationTokenSource ,在 Task.Run 執行前如果使用 cancellationTokenSource.Cancel() 方法標記取消,Task.Run 就會不會執行,但是會拋出 TaskCanceledException 例外:
CancellationToken 被包裝在 CancellationTokenSource 內,需要透過 CancellationTokenSource 來使用。CancellationTokenSource.Token 就是 CancellationToken
也可以註冊取消時會觸發的事件:
剛剛是任務已經取消,才輪到 Task.Run 執行,因為在傳入的參數發現任務已取消,所以不執行任務,直接拋出例外。
那如果是任務執行到一半發現要取消可以怎麼做?
假設有任務按步驟順序是 1, 2-1, 2-2, 3 ,在步驟 1 到 2-1 中間和 2-2 到 3 之間如果要取消任務則可以直接終止任務,那可以使用手動判斷的方式:
那 Task.Delay 到一半可以終止嗎?
上面的 Task.Delay 都是用來模擬需要耗費一段時間的任務,但是假設在程式碼中真的會使用到 Task.Delay ,可以在 Task.Delay 中傳入 cancellationTokenSource.Token 參數,如果中途任務被取消,就不會等到任務結束,而是會直接拋出 TaskCanceledException 例外,省去了剩餘的等待時間
在日常使用中假設有一個自訂的 MyTask:
我們可以這樣使用:
假設確定絕對不會中途取消,也不需要先建立 CancellationTokenSource ,可以直接這樣:
參考資料:
Microsoft.Learn - CancellationToken Struct
Microsoft.Learn - CancellationTokenSource Class
Microsoft.Learn - OperationCanceledException Class
Microsoft.Learn - Cancellation in Managed Threads
await Task.Run(async () =>
{
Console.WriteLine("Step 1");
await Task.Delay(1000);
Console.WriteLine("Step 2");
});
可以使用 CancellationTokenSource ,在 Task.Run 執行前如果使用 cancellationTokenSource.Cancel() 方法標記取消,Task.Run 就會不會執行,但是會拋出 TaskCanceledException 例外:
CancellationTokenSource cancellationTokenSource = new();
cancellationTokenSource.Cancel(); // 取消任務
try
{
await Task.Run(async () =>
{
Console.WriteLine("步驟 1");
await Task.Delay(1000); // 假裝是耗時的任務
Console.WriteLine("步驟 2");
}, cancellationTokenSource.Token);
}
catch (System.Threading.Tasks.TaskCanceledException)
{
Console.WriteLine("任務已取消");
}
CancellationToken 被包裝在 CancellationTokenSource 內,需要透過 CancellationTokenSource 來使用。CancellationTokenSource.Token 就是 CancellationToken
也可以註冊取消時會觸發的事件:
CancellationTokenSource cancellationTokenSource = new();
cancellationTokenSource.Token.Register(() => Console.WriteLine("已取消"));
剛剛是任務已經取消,才輪到 Task.Run 執行,因為在傳入的參數發現任務已取消,所以不執行任務,直接拋出例外。
那如果是任務執行到一半發現要取消可以怎麼做?
假設有任務按步驟順序是 1, 2-1, 2-2, 3 ,在步驟 1 到 2-1 中間和 2-2 到 3 之間如果要取消任務則可以直接終止任務,那可以使用手動判斷的方式:
CancellationTokenSource cancellationTokenSource = new();
// 500 毫秒後取消任務
Task.Delay(500).ContinueWith(_ => cancellationTokenSource.Cancel());
try
{
await Task.Run(async () =>
{
Console.WriteLine("步驟 1");
await Task.Delay(1000); // 假裝是耗時的任務
if (cancellationTokenSource.Token.IsCancellationRequested)
return;
Console.WriteLine("步驟 2-1");
await Task.Delay(1000);
Console.WriteLine("步驟 2-2");
await Task.Delay(1000);
if (cancellationTokenSource.Token.IsCancellationRequested)
return;
Console.WriteLine("步驟 3");
}, cancellationTokenSource.Token);
}
catch (TaskCanceledException e)
{
Console.WriteLine(e);
Console.WriteLine("任務已取消");
}
那 Task.Delay 到一半可以終止嗎?
上面的 Task.Delay 都是用來模擬需要耗費一段時間的任務,但是假設在程式碼中真的會使用到 Task.Delay ,可以在 Task.Delay 中傳入 cancellationTokenSource.Token 參數,如果中途任務被取消,就不會等到任務結束,而是會直接拋出 TaskCanceledException 例外,省去了剩餘的等待時間
CancellationTokenSource cancellationTokenSource = new();
await Task.Delay(1000, cancellationTokenSource.Token);
在日常使用中假設有一個自訂的 MyTask:
async Task MyTask(CancellationToken cancellationToken)
{
Console.WriteLine("步驟 1");
await Task.Delay(1000); // 假裝是耗時的任務
if (cancellationToken.IsCancellationRequested)
return;
Console.WriteLine("步驟 2-1");
Console.WriteLine("步驟 2-2");
await Task.Delay(1000);
if (cancellationToken.IsCancellationRequested)
return;
Console.WriteLine("步驟 3");
}
我們可以這樣使用:
CancellationTokenSource cancellationTokenSource = new();
await MyTask(cancellationTokenSource.Token);
假設確定絕對不會中途取消,也不需要先建立 CancellationTokenSource ,可以直接這樣:
await MyTask(CancellationToken.None);
參考資料:
Microsoft.Learn - CancellationToken Struct
Microsoft.Learn - CancellationTokenSource Class
Microsoft.Learn - OperationCanceledException Class
Microsoft.Learn - Cancellation in Managed Threads
感謝教學~
回覆刪除