0

現在、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
4

1 に答える 1

0

まず、ロジックをIDに基づいて作成するべきではないと思います(本当に必要な場合を除く)。SortedRecommendationsコレクション内のオブジェクトへの参照を使用する必要があります。

これで、一度に1つのファイルだけをダウンロードしたい場合は、次を使用できます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);

最善の解決策は、1つまたはすべてではなく、限られた数のファイルを一度にダウンロードすることです。これを行うのはより複雑であり、1つの解決策は、setを使用ActionBlockしてTPLDataflowから使用することMaxDegreeOfParallelismです。

于 2012-07-20T13:24:16.567 に答える