C# 中取消任務(wù)(Task)的正確方式
在C#中,異步編程是處理耗時(shí)操作,如I/O請(qǐng)求、數(shù)據(jù)庫(kù)調(diào)用或網(wǎng)絡(luò)請(qǐng)求等,而不阻塞主線程的一種有效方法。Task 類是這種異步操作的核心,它允許我們啟動(dòng)異步操作并等待其完成。然而,有時(shí)我們可能需要在任務(wù)完成之前取消它,特別是當(dāng)任務(wù)依賴于某些外部條件或用戶交互時(shí)。在C#中,取消任務(wù)通常通過(guò)使用 CancellationToken 來(lái)實(shí)現(xiàn)。
CancellationToken 和 CancellationTokenSource
CancellationToken 是一個(gè)結(jié)構(gòu),用于傳遞取消操作的通知,如用戶請(qǐng)求取消或超時(shí)。CancellationTokenSource 是用于生成 CancellationToken 的類,并提供了取消該令牌的方法。
下面是一個(gè)簡(jiǎn)單的示例,展示了如何使用 CancellationToken 和 CancellationTokenSource 來(lái)取消一個(gè)任務(wù):
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
using CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken cancellationToken = cts.Token;
// 啟動(dòng)一個(gè)可以被取消的任務(wù)
Task myTask = Task.Run(() => DoWork(cancellationToken), cancellationToken);
// 假設(shè)一段時(shí)間后,我們決定取消任務(wù)
await Task.Delay(2000); // 等待2秒
Console.WriteLine("Cancelling the task...");
cts.Cancel(); // 發(fā)送取消信號(hào)
try
{
await myTask; // 等待任務(wù)完成或捕獲到OperationCanceledException異常
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was cancelled");
}
}
static void DoWork(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Cancellation requested.");
// 檢查取消標(biāo)記,如果已請(qǐng)求取消,則退出循環(huán)或執(zhí)行其他清理操作
break; // 或者返回,拋出OperationCanceledException等。
}
// 模擬工作正在進(jìn)行中...
Thread.Sleep(500); // 不要在生產(chǎn)代碼中使用Thread.Sleep! 這里只是為了示例。
Console.WriteLine("Working...");
}
}
}
在這個(gè)示例中,我們創(chuàng)建了一個(gè) CancellationTokenSource 實(shí)例,并使用其 Token 屬性生成了一個(gè) CancellationToken。然后,我們將這個(gè)令牌傳遞給了一個(gè)在后臺(tái)運(yùn)行的任務(wù)(通過(guò) Task.Run)。稍后,我們決定取消這個(gè)任務(wù),于是調(diào)用了 CancellationTokenSource.Cancel 方法來(lái)提供取消信號(hào)。在任務(wù)代碼中,我們定期檢查取消標(biāo)記,如果已請(qǐng)求取消,則退出循環(huán)。
注意事項(xiàng)和最佳實(shí)踐:
- 定期檢查取消標(biāo)記:在你的任務(wù)代碼中,你應(yīng)該定期檢查 CancellationToken.IsCancellationRequested 屬性,以便在收到取消請(qǐng)求時(shí)能夠迅速響應(yīng)。
- 處理取消請(qǐng)求:當(dāng)檢測(cè)到取消請(qǐng)求時(shí),你的代碼應(yīng)該盡快停止當(dāng)前的操作并退出。你可以通過(guò)拋出 OperationCanceledException 異常、返回或執(zhí)行其他適當(dāng)?shù)那謇聿僮鱽?lái)實(shí)現(xiàn)這一點(diǎn)。
- 使用正確的等待方式:在等待可能被取消的任務(wù)時(shí),最好使用 await 關(guān)鍵字而不是 Task.Wait() 或 Task.Result,因?yàn)楹笳咴谌蝿?wù)被取消時(shí)會(huì)拋出 AggregateException 而不是 OperationCanceledException,這可能會(huì)使異常處理更加復(fù)雜。
- 資源清理:確保在取消操作后妥善處理和清理所有已分配的資源,以避免內(nèi)存泄漏或其他潛在問(wèn)題。這包括關(guān)閉文件句柄、釋放數(shù)據(jù)庫(kù)連接等。
- 文檔和測(cè)試:如果你的方法接受一個(gè) CancellationToken 參數(shù),確保在方法的文檔中明確說(shuō)明這一點(diǎn),并編寫針對(duì)取消操作的單元測(cè)試,以確保你的代碼在收到取消信號(hào)時(shí)能夠正確響應(yīng)。