CancellationToken
通常在异步场景下,我们需要提前终止任务。如:请求超时提前终止任务,防止一直占用资源、用户主动取消操作等。
这里就可以使用 CancellationToken 参数,用于获得提前终止执行的信号。
转到定义可以看到 CancellationToken 是一个结构体,可用于终止线程的成员有:
IsCancellationRequested 属性
该属性用来判断 CancellationToken 是否发出取消任务的请求。使用案例:
/// <summary>
/// 下载 n 次指定 url 的内容
/// </summary>
/// <param name="url"></param>
/// <param name="n"></param>
/// <param name="cancellationToken">取消操作的通知</param>
/// <returns></returns>
public static async Task DownloadHtmlAsync(string url, int n, CancellationToken cancellationToken)
{
using HttpClient httpClient = new HttpClient();
for (int i = 0; i < n; i++)
{
string html = await httpClient.GetStringAsync(url);
Console.WriteLine($"{DateTime.Now},第{i + 1}次:{html.Substring(0, 20)}");
if (cancellationToken.IsCancellationRequested) // 判断是否发出了取消通知,需要手动的处理取消
{
Console.WriteLine("请求被取消");
break;
}
await Task.Delay(200);
}
}
通过 CancellationTokenSource 类的实例创建 CancellationToken 信号。调用:
CancellationTokenSource ctSource = new CancellationTokenSource();
ctSource.CancelAfter(1000 * 1); // 在指定的毫秒数之后取消
await HttpClientHelper.DownloadHtmlAsync("http://www.baidu.com", 10, ctSource.Token);
ThrowIfCancellationRequested方法
CancellationToken 还有一个实例方法 ThrowIfCancellationRequested,用法:
public static async Task DownloadHtmlAsync(string url, int n, CancellationToken cancellationToken)
{
using HttpClient httpClient = new HttpClient();
for (int i = 0; i < n; i++)
{
string html = await httpClient.GetStringAsync(url);
Console.WriteLine($"{DateTime.Now},第{i + 1}次:{html.Substring(0, 20)}");
cancellationToken.ThrowIfCancellationRequested(); // 改动
await Task.Delay(200);
}
}
再次运行可以看到抛出了一个异常:System.OperationCanceledException:“The operation was canceled.”
我们反编译看看 ThrowIfCancellationRequested 内部的原理,发现方法内部同样是判断了 IsCancellationRequested 属性:
建议使用 IsCancellationRequested,因为思路更加清晰,并且可以进行其他额外的精确控制。而通过方法抛出异常,需要先 catch 异常在进行处理,没有直接判断属性来的直接高效
Cancel
立即发出终止任务的信号。
CancellationTokenSource ctSource = new CancellationTokenSource();
HttpClientHelper.DownloadHtmlAsync("http://www.baidu.com", 10, ctSource.Token);
Console.ReadKey();
ctSource.Cancel(); // 按任意键取消请求
Console.ReadKey();