如何将CancellationTokenSource附加到DownloadStringTaskAsync方法并取消异步调用?

20

我正在创建一个示例,使用WebClient使用async和await方法来调用链接,现在我想附加取消异步调用的功能。但是我无法获取CancellationTokenSource令牌并将DownloadStringTaskAsync连接到此取消令牌。以下是我的代码,请问如何实现。

private async void DoWork()
        {
            this.Cursor = Cursors.WaitCursor;
            Write("DoWork started.");
            cts = new CancellationTokenSource();
            WebClient wc = new WebClient();
            string result = await wc.DownloadStringTaskAsync(new Uri("http://gyorgybalassy.wordpress.com"));

            if (result.Length < 100000)
            {
                Write("The result is too small, download started from second URL.");
                result = await wc.DownloadStringTaskAsync(new Uri("https://www.facebook.com/balassy"));
            }
            Write("Download completed. Downloaded bytes: " + result.Length.ToString());
            Write("DoWork ended.");
            this.Cursor = Cursors.Default;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            Write("Cancellation started.");
            this.cts.Cancel();
            Write("Cancellation ended.");
        }
当我的取消按钮调用cts.Cancel时,DownloadStringTaskAsync调用并没有被取消。为什么取消按钮不能取消异步调用?

你没有以任何方式使用CancellationTokeSource,那么WebClient怎么知道当你没有告诉它时应该取消呢? - svick
感谢svick的回复。但是我尝试将令牌作为DownloadStringTaskAsync方法的参数传递,但是没有支持它的重载方法。因此,我不知道如何在DownloadStringTaskAsync方法中使用Cancellation token。你能推荐一些好书来阅读C#中所有这些新的TAP功能更新吗? - Balraj Singh
3个回答

31

WebClient的异步能力早于 .Net 4.5,因此它仅部分支持基于任务的异步模式。其中包括具有自己的取消机制:CancelAsync() 方法,即使使用新的 -TaskAsync方法也有效。要在 CancellationToken 被取消时调用此方法,可以使用Register() 方法

cts.Token.Register(wc.CancelAsync);

作为一种替代方案,你可以使用新的HttpClient,正如Stephen所建议的那样,它完全支持TAP,包括CancellationToken


4
在下载完成后处理 cts.Token.Register 返回的 CancellationTokenRegistration ,将不会有任何问题。 - Brian

5

基于svick答案的扩展方法:

public static async Task<string> DownloadStringTaskAsync(this WebClient webClient, string url, CancellationToken cancellationToken) {
    using (cancellationToken.Register(webClient.CancelAsync)) {
        return await webClient.DownloadStringTaskAsync(url);
    }
}

public static async Task<string> DownloadStringTaskAsync(this WebClient webClient, Uri uri, CancellationToken cancellationToken) {
    using (cancellationToken.Register(webClient.CancelAsync)) {
        return await webClient.DownloadStringTaskAsync(uri);
    }
}

这实际上是微软推荐的方式,请看示例代码,它甚至使用WebClient作为示例! - Alex from Jitbit

4

WebClient 不支持取消操作。我建议您使用较新的类型,例如 HttpClient

...
cts = new CancellationTokenSource();
string result;
using (var client = new HttpClient())
using (var response = await client.GetAsync("http://gyorgybalassy.wordpress.com", cts.Token))
{
  result = await response.Content.ReadAsStringAsync();
}

if (result.Length < 100000)
...
< p > 默认情况下,GetAsync方法不会在读取完整个响应之前完成,因此await response.Content.ReadAsStringAsync行实际上会同步完成。


小细节:HttpClient设计用于重复使用,不要将其放在using中。 - Alex from Jitbit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接