" 返回《C 併發編程》" "1. 取消請求" "2. 超時後取消" "3. 取消並行" "4. 取消響應式代碼" "5. 與其他取消體系的互操作" 是一個等同於 預設 的特殊值,表示這個方法是永遠不會被取消的。 實例代碼 輸出: 1. 取消請求 2. 超時後取消 輸出: 只要執行代碼時用到了超時, ...
CancellationToken.None
是一個等同於預設的特殊值,表示這個方法是永遠不會被取消的。
實例代碼
static async Task CancelableMethodAsync(CancellationToken token)
{
await Task.Delay(1000, token);
throw new ArgumentException();
}
public static async Task IssueCancelRequestAsync()
{
var cts = new CancellationTokenSource();
var task = CancelableMethodAsync(cts.Token);
// 這裡,操作在正常運行。
// 發出取消請求。
cts.Cancel();
//(非同步地)等待操作結束。
try
{
await task;
// 如運行到這裡,說明在取消請求生效前,操作正常完成 。
}
catch (OperationCanceledException ex)
{
// 如運行到這裡,說明操作在完成前被取消。
System.Console.WriteLine(ex.GetType().Name);
}
catch (Exception ex)
{
// 如運行到這裡,說明在取消請求生效前,操作出錯並結束。
System.Console.WriteLine(ex.GetType().Name);
}
}
輸出:
TaskCanceledException
1. 取消請求
public static int CancelableMethod(CancellationToken cancellationToken)
{
for (int i = 0; i != 100000; ++i)
{
// cancellationToken.WaitHandle.WaitOne(1000);
Thread.Sleep(1);
// 這裡做一些計算工作。
if (i % 1000 == 0)
cancellationToken.ThrowIfCancellationRequested();
}
return 42;
}
2. 超時後取消
public static async Task IssueTimeoutAsync()
{
Stopwatch sw = Stopwatch.StartNew();
try
{
var cts = new CancellationTokenSource();
var token = cts.Token;
cts.CancelAfter(TimeSpan.FromSeconds(2));
await Task.Delay(TimeSpan.FromSeconds(4), token);
}
finally
{
System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
}
}
輸出:
2004ms
Unhandled Exception: ... ...
只要執行代碼時用到了超時,就該使用 CancellationTokenSource
和 CancelAfter
(或者用構造函數)。雖然還有其他途徑可實現這個功能,但是使用現有的取消體系是最簡單也是最高效的。
3. 取消並行
public class Matrix
{
public void Rotate(float degrees) { }
}
//只做展示
public static void RotateMatrices(IEnumerable<Matrix> matrices, float degrees, CancellationToken token)
{
Parallel.ForEach(matrices, new ParallelOptions
{
CancellationToken = token
},
matrix => matrix.Rotate(degrees));
}
4. 取消響應式代碼
註入取消請求
- 某一個層次的代碼需要響應取消請求,同時它本身也要向下一層代碼發出取消請求(但不會向上傳遞)。
public static async Task RunGetWithTimeoutAsync()
{
CancellationTokenSource source = new CancellationTokenSource();
await GetWithTimeoutAsync("http://www.baidu.com", source.Token);
}
public static async Task<HttpResponseMessage> GetWithTimeoutAsync(string url, CancellationToken cancellationToken)
{
var client = new HttpClient();
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
{
cts.CancelAfter(TimeSpan.FromMilliseconds(100));
var combinedToken = cts.Token;
return await client.GetAsync(url, combinedToken);
}
}
輸出:
Unhandled Exception: Unhandled exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.AggregateException: One or more errors occurred. (A task was canceled.) ---> System.Threading.Tasks.TaskCanceledException ... ...
5. 與其他取消體系的互操作
有一些外部的或以前遺留下來的代碼採用了非標準的取消模式。現在要用標準的CancellationToken 來控制這些代碼
public static async Task RunPingAsync()
{
var cts = new CancellationTokenSource();
var task = PingAsync("192.168.0.101", cts.Token);
//cts.Cancel();
await task;
}
public static async Task<PingReply> PingAsync(string hostNameOrAddress, CancellationToken cancellationToken)
{
Stopwatch sw = Stopwatch.StartNew();
try
{
var ping = new Ping();
using (cancellationToken.Register(() => ping.SendAsyncCancel()))
{
return await ping.SendPingAsync(hostNameOrAddress);
}
}
finally
{
System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");
}
}
註意: 為了避免記憶體和資源的泄漏,一旦不再需要使用回調函數了,就要釋放這個回調函數註冊。