2

次の状況があります (または async await メカニズムに関する基本的な誤解)。

時間がかかる 1 ~ 20 の Web 要求呼び出しのセットがあるとしますfindItemsByProduct()。これらすべての呼び出しを 1 つの非同期呼び出しに抽象化できる非同期要求でそれをラップしたいのですが、より多くのスレッドを使用しないとそれを実行できないようです。

私がやっている場合:

 int total = result.paginationOutput.totalPages;
 for (int i = 2; i < total + 1; i++)
     {

      await Task.Factory.StartNew(() =>
      {
         result = client.findItemsByProduct(i);
      });
      newList.AddRange(result.searchResult.item);

      }
     }
 return newList;

ここでの問題は、呼び出しが一緒に実行されず、1 つずつ待機していることです。すべての呼び出しを一緒に実行して、結果を収集したいと思います。

疑似コードとして、コードを次のように実行したいと思います。

forEach item {
  result = item.makeWebRequest();
}
foreach item {
  List.addRange(item.harvestResults);
}

私はそれを行うためのコードを作成する方法がわかりません..

4

4 に答える 4

1

私が見ているあなたの要件を考えると:

  • n個のノンブロッキング タスクを処理する
  • すべてのクエリが返された後に結果を処理する

これにはCountdownEventを使用します。

var results = new ConcurrentBag<ItemType>(result.pagination.totalPages);
using (var e = new CountdownEvent(result.pagination.totalPages))
{
    for (int i = 2; i <= result.pagination.totalPages+1; i++)
    {
        Task.Factory.StartNew(() => return client.findItemsByProduct(i))
                    .ContinueWith(items => {
                        results.AddRange(items);
                        e.Signal(); // signal task is done
                    });
    }
    // Wait for all requests to complete
    e.Wait();
}
// Process results
foreach (var item in results) 
{
    ...
}
于 2013-06-25T15:46:53.217 に答える
1

findItemsByProductAsyncを返す aを追加するのが理想的ですTask<Item[]>StartNewそうすれば、またはを使用して不要なタスクを作成する必要がなくなりますTask.Run

次に、コードは次のようになります。

int total = result.paginationOutput.totalPages;

// Start all downloads; each download is represented by a task.
Task<Item[]>[] tasks = Enumerable.Range(2, total - 1)
    .Select(i => client.findItemsByProductAsync(i)).ToArray();

// Wait for all downloads to complete.
Item[][] results = await Task.WhenAll(tasks);

// Flatten the results into a single collection.
return results.SelectMany(x => x).ToArray();
于 2013-06-25T16:46:57.043 に答える
0

この特定の問題は、 を使用しなくても簡単に解決できawaitます。各タスクを作成し、すべてのタスクをWhenAllリストに入れ、そのリストで使用して、それらすべてのタスクの完了を表すタスクを取得します。

public static Task<Item[]> Foo()
{
    int total = result.paginationOutput.totalPages;

    var tasks = new List<Task<Item>>();

    for (int i = 2; i < total + 1; i++)
    {
        tasks.Add(Task.Factory.StartNew(() => client.findItemsByProduct(i)));
    }

    return Task.WhenAll(tasks);
}

resultまた、コードでの使用方法に大きな問題があることに注意してください。さまざまなタスクのそれぞれがすべて同じ変数を使用しているため、それが適切に機能するかどうかについて競合状態があります。同じ呼び出しを 2 回追加すると、1 つが完全にスキップされる可能性があります。代わりに、呼び出しをfindItemsByProductタスクの結果にする必要があり、そのタスクのResult.

于 2013-06-25T16:02:27.020 に答える