【问题标题】:UserState using WebClient and TaskAsync download from Async CTPUserState 使用 WebClient 和 TaskAsync 从 Async CTP 下载
【发布时间】:2012-07-20 21:24:16
【问题描述】:

我目前正在使用 Async CTP,需要将此代码转换为可以使用 Task.WhenAll() 的代码。

到目前为止,我所做的是使用 UserState 对象并将我的标识符 (AID) 放入其中,然后在完成的事件中使用它。

但是 wc.DownloadFileTaskAsync 方法没有 UserState 的重载。我能做什么?

for (int i = 0; i < SortedRecommendations.Count; i++)
{
    string tempfilepath = filepath + SortedRecommendations[i].Aid + ".jpg";

    if (File.Exists(tempfilepath))
        continue;

    WebClient wc = new WebClient();
    wc.DownloadFileCompleted += (s, e) =>
        {
            var q = SortedRecommendations.Where(x => x.Aid == (int)e.UserState);
            if (q.Count() > 0)
                q.First().Image = tempfilepath;
        };
    wc.DownloadFileAsync(new Uri(SortedRecommendations[i].Image.Replace("t.jpg", ".jpg")), tempfilepath, SortedRecommendations[i].Aid);
}

这基本上是我想出的,但是我在 y.Aid == SortedRecommendations[i].Aid 得到了一个超出范围的异常,因为我现在显然是在下载开始时的其他东西。我看到的唯一其他可能性是使用类似 TaskEx.Run( () => { // 同步下载数据 }; 但我不喜欢这种方法。

for (int i = 0; i < SortedRecommendations.Count; i++)
{
    string tempfilepath = filepath + SortedRecommendations[i].Aid + ".jpg";

    if (File.Exists(tempfilepath))
        continue;

    WebClient wc = new WebClient();
    wc.DownloadFileCompleted += (s, e) =>
    {
        var q = SortedRecommendations.Where(x => x.Aid == SortedRecommendations[i].Aid);
        if (q.Count() > 0)
            q.First().Image = tempfilepath;

    };
    tasks.Add(wc.DownloadFileTaskAsync(new Uri(SortedRecommendations[i].Image.Replace("t.jpg", ".jpg")), tempfilepath));
}

await TaskEx.WhenAll(tasks);
//Everything finished

【问题讨论】:

  • 如果可以的话,你应该使用 VS2012 RC 而不是 CTP。它包含许多改进和错误修复。
  • 另外,您确定要同时开始下载所有文件吗?
  • 是的,我想同时下载所有文件(+ afaik ctp 使用线程池并选择适当数量的并行线程。)。而且我不能使用 .NET 4.5,因为我依赖 Blend。 Blend,即使是您使用 VS12 获得的那种,也不能在 .NET 4.5 中正常工作。 (发现这个很难...)
  • 它确实使用ThreadPool(在正常情况下),但它可以选择适当数量的线程,仅用于短的CPU密集型操作。它根本不适用于异步 IO 操作,因为异步部分不会使用任何线程。因此,您的所有文件将几乎同时开始下载。

标签: c# asynchronous webclient async-ctp


【解决方案1】:

首先,我认为您不应该将您的逻辑建立在 id 上(除非您真的必须这样做)。您应该使用对 SortedRecommendations 集合中对象的引用。

现在,如果您想一次只下载一个文件,您可以简单地使用await

for (int i = 0; i < SortedRecommendations.Count; i++)
{
    string tempfilepath = filepath + SortedRecommendations[i].Aid + ".jpg";

    if (File.Exists(tempfilepath))
        continue;

    WebClient wc = new WebClient();
    var recommendation = SortedRecommendations[i];
    await wc.DownloadFileTaskAsync(new Uri(recommendation.Image.Replace("t.jpg", ".jpg")), tempfilepath);
    recommendation.Image = tempfilepath;
}

但是,如果您想同时开始所有下载,就像您的 DownloadFileAsync() 代码一样,您可以改用 ContinueWith()。而且你不需要用户状态,这就是闭包的用途:

for (int i = 0; i < SortedRecommendations.Count; i++)
{
    string tempfilepath = filepath + SortedRecommendations[i].Aid + ".jpg";

    if (File.Exists(tempfilepath))
        continue;

    WebClient wc = new WebClient();
    var recommendation = SortedRecommendations[i];
    var downloadTask = wc.DownloadFileTaskAsync(new Uri(recommendation.Image.Replace("t.jpg", ".jpg")), tempfilepath);
    var continuation = downloadTask.ContinueWith(t => recommendation.Image = tempfilepath);
    tasks.Add(continuation);
}

await Task.WhenAll(tasks);

最好的解决方案可能是一次下载有限数量的文件,而不是一个或全部。这样做更复杂,一种解决方案是使用 TPL Dataflow 中的 ActionBlock 并设置 MaxDegreeOfParallelism

【讨论】:

  • 谢谢!不知道 ContinueWith。!
猜你喜欢
  • 1970-01-01
  • 2012-11-27
  • 2011-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多