以并行方式处理一组异步调用的正确方法

时间:2015-06-18 14:59:57

标签: c# parallel-processing async-await task

我有一个调用web api的控制台应用程序并获取服务列表。然后它循环并调用每个服务。

我有以下内容:

static int Main(string[] args)
{
   ...       
   Task.WaitAll(Process());
}

private static async Task BeginProcess()
{
   using(HttpClientHandler handler = new HttpClientHandler())
   {
      handler.UseDefaultCredentials = true;
      using(var client = new HttpClient(handler))
      {
         var response = client.GetStringAsync(_baseUrl);

         List<Service> services = new List<Service>();
         services = jss.Deserialize<List<Service>>(response.Result);

         client.Timeout = new TimeSpan(0,3,0);
         foreach(var service in services)
         {
            Console.WriteLine("Running " + service.Name);
            var _serviceResponse = await client.PostAsync(_baseURL + service.Id.ToString(), null);
            Console.WriteLine(service.Name + " responded with " + _serviceRepsonse.StatusCode);
         }
      }
    }
}

不幸的是,此代码按顺序处理每个服务,而不是并行调用。问题是,我不确定如何使这些调用并行运行。

4 个答案:

答案 0 :(得分:3)

虽然我接受了答案,但我终于登上了这个我觉得更简洁的。

await Task.WhenAll(services.Select(async s => {
   Console.WriteLine("Running " + s.Name);
   var _serviceResponse = await client.PostAsync(...);
   Console.WriteLine(s.Name + " responded with " + _serviceResponse.StatusCode);
}));

答案 1 :(得分:2)

答案可以在C#cookbook中的Concurrency中找到:

static async Task<string> DownloadAllAsync(IEnumerable<string> urls){
    var httpClient = new HttpClient();

    var downloads = urls.select(url => httpClient.getStringAsync(url));
    Task<string>[] tasks = downloads.ToArray(); //-> tasks are started
    //now that you have an array of tasks you can wait for them all to finish
    string[] htmlPages = await Task.WhenAll(tasks);
    return string.Concat(htmlPages); 
}

这里的关键点是:

  • 安排代码以获取任务数组(不同容器也可以,它不一定是数组)
  • 使用await Task.WhenAll(任务数组);

答案 2 :(得分:1)

正如@SLaks所提到的,你需要用这些行代替你的循环...

     var asyncTasks = new Dictionary<Service, Task>();
     foreach(var service in services)
     {
        Console.WriteLine("Running " + service.Name);
       asyncTasks.Add(service, client.PostAsync(_baseURL + service.Id.ToString(), null));
     }
     // All tasks are running, so wait for all of them to finish here
     await Task.WhenAll(asyncTasks);

     foreach(var service in asyncTasks.Keys) {
        Console.WriteLine("Service " + service.Name + " returned " + syncTasks[service].Result);
     }

希望它有所帮助。

答案 3 :(得分:0)

改变这个:

foreach(var service in services)
{
    Console.WriteLine("Running " + service.Name);
    var _serviceResponse = await client.PostAsync(_baseURL + service.Id.ToString(), null);
    Console.WriteLine(service.Name + " responded with " + _serviceRepsonse.StatusCode);
}

到此:

var serviceCallTaskList = new List<Task<HttpResponseMessage>>();
foreach(var service in services)
{
    Console.WriteLine("Running " + service.Name);
    serviceCallTaskList.Add(client.PostAsync(_baseURL + service.Id.ToString(), null));
}

HttpResponseMessage[] responseArray = await Task.WhenAll(serviceCallTaskList);

for(int i = 0; i < responseArray.Length; i++)
{
    Console.WriteLine(services[i].Name + " responded with " + responseArray[i].StatusCode);
}