【问题标题】:How can I cancel and progress report a task that returned by WebClient.DownloadDataTaskAsync method?如何取消和进度报告由 WebClient.DownloadDataTaskAsync 方法返回的任务?
【发布时间】:2015-12-16 18:26:02
【问题描述】:

我正在学习任务并行库。我有一些使用 WebClient 类从 Web 下载数据的旧代码。我想将我以前使用Event-based Asynchronous Pattern(EAP) 的代码转换为Task-based Asynchronous Pattern (TAP)

我的旧代码如下:

WebClient client1 = new WebClient();
client1.DownloadDataCompleted += (o, e)=>
{
    if (e.Cancelled)
    {
        //code that update UI report download has been canceled.
    }
    else
    {
        byte[] s = e.Result;
        //code that update UI report downloads has been completed.
    }
};

client1.DownloadProgressChanged += ( o,  e) =>
{
    //code that update UI report downloading progress.
    updateProgress(e.ProgressPercentage);
};

//start download asynchronous
client1.DownloadDataAsync(new Uri("http://stackoverflow.com/"));

//code to cancel download.
client1.CancelAsync();

现在使用任务 API,我有代码:

WebClient client2 = new WebClient();
Task<byte[]> task = client2.DownloadDataTaskAsync("http://stackoverflow.com/");

task.ContinueWith((antecedent) =>
{
    byte[] s = antecedent.Result;
    //code that updateUI report download has been completed.
});

//TODO how to code that can cancel the download and report progress?

所以我的问题是:

使用任务方法DownloadDataTaskAsync时,WebClient类是否有内置api可以取消下载并报告下载进度?

【问题讨论】:

标签: c# .net task-parallel-library


【解决方案1】:

您可以扩展 WebClient:

public static class WebClientExtensions
{
    public static async Task<byte[]> DownloadDataTaskAsync(this WebClient webClient, string address, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();

        using (cancellationToken.Register(webClient.CancelAsync))
        {
            return await webClient.DownloadDataTaskAsync(address);
        }
    }
}

然后调用扩展方法:

var data = await webClient.DownloadDataTaskAsync("http://stackoverflow.com/", cancellationToken);

【讨论】:

  • 通常你会想把你的注册包裹在一个using块中,这样一旦你离开这个块,它就会被取消注册。无需更改任何其他内容,只需将using 放入并将return await obj... 放入块中即可。
  • 我同意。应处置一次性物品。我添加了一个 using 块。
【解决方案2】:

没有支持取消的重载,因此您无法取消该异步操作(您可能需要阅读How do I cancel non-cancelable async operations?)。

但是,您可以在该操作之上添加超时或取消,以使您的代码流继续进行。 对于进度报告,您可能需要查看WebClient.DownloadProgressChanged Event。它似乎符合您的需求。

【讨论】:

    【解决方案3】:
    public static async Task<byte[]> DownloadDataTaskAsync(this WebClient obj, Uri address, CancellationToken cancellationToken)
        {
            var tcs = new TaskCompletionSource<bool>();
    
            var task = obj.DownloadDataTaskAsync(address);
    
            using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
            {
                if (task != await Task.WhenAny(task, tcs.Task))
                {
                    throw new OperationCanceledException(cancellationToken);
                }
            }
    
            return await task;
        }
    

    来自@Dartal 的修改版

    使用任务完成源解决问题,也可以参考How do I cancel non-cancelable async operations?

    然后,您可以使用 CancelAsync 强制关闭连接

    Aborting a WebClient.DownloadFileAsync operation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-07
      • 1970-01-01
      • 1970-01-01
      • 2021-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多