简体   繁体   English

如何获取异步代码以等待所有任务完成?

[英]How do I get async code to wait until all tasks are complete?

When I run the code below, the message "Press Enter to continue... " appears before the results are returned from the HttpClient.GetAsync() calls are completed. 当我运行下面的代码时,在从HttpClient.GetAsync()调用返回结果之前,会出现消息"Press Enter to continue... " The actual sequence of events: the GetAsync() calls are made, the " Press Enter... " message appears, and then the results are one-by-one output to the console window. 实际的事件顺序:进行GetAsync()调用,出现“ Press Enter... ”消息,然后将结果一一输出到控制台窗口。 How do I wait until all the GetAsync() calls are complete before displaying the " Press Enter... " message? 在显示“ Press Enter... ”消息之前,如何等待直到所有GetAsync()调用完成?

class Program
{
  static HttpClient client = new HttpClient();

  static void Main(string[] args)
  {
    RunAsync().Wait();

    Console.WriteLine("\n\n\n\nPress Enter to continue... ");
    Console.ReadLine();
  }

  static async Task RunAsync()
  {
    List<string> urls = new List<string>()
    {
      "http://www.domain1.com",
      "http://www.domain2.com",
      "http://www.domain3.com",
      "http://www.domain4.com"
    };

    foreach (var url in urls)
    {
      DownloadPageAsync(url);
    }
  }

  static async Task<string> DownloadPageAsync(string url)
  {
    Console.WriteLine("Starting: " + url);

    HttpResponseMessage response = await client.GetAsync(url);

    if (response.IsSuccessStatusCode)
    {
      // do stuff here
    }

    Console.WriteLine("Done: " + url);

    return response.Content.ToString();
  }
}

Since DownloadPageAsync returns a task, you can make a list of all tasks and wait on them all: 由于DownloadPageAsync返回任务,因此您可以列出所有任务并等待所有任务:

Task.WhenAll(urls.Select(url => DownloadPageAsync(url)))

Or simplified: 或简化:

Task.WhenAll(urls.Select(DownloadPageAsync))

The @patrik-hofman answer is a good one (up voted) although, see my comment @ patrik-hofman的答案是一个很好的答案(已投票通过),请参阅我的评论

If you would rather the requests to happen sequentially... Add await to the DownloadPageAsync line. 如果您希望请求按顺序发生,请...在DownloadPageAsync行中添加await

You've used async in RunAsync but there are no await s. 您在RunAsync使用了async ,但是没有await So, although it returns a Task it is not waiting for the DownloadPageAsync call to complete. 因此,尽管它返回一个Task,但它并不等待DownloadPageAsync调用完成。 This means that the method simply returns an "empty" Task which completes immediately. 这意味着该方法仅返回立即完成的“空”任务。 So your .Wait() is waiting for nothing. 因此,您的.Wait()没有任何等待。

I think the problem is that you are not awaiting the DownloadPageAsync method inside the RunAsync() method. 我认为问题在于您没有等待RunAsync()方法中的DownloadPageAsync方法。 If you update the RunAsync() method to the code below then I believe it will work as you expect: 如果将RunAsync()方法更新为以下代码,那么我相信它会按预期工作:

static async Task RunAsync()
{
    List<string> urls = new List<string>()
    {
      "http://www.domain1.com",
      "http://www.domain2.com",
      "http://www.domain3.com",
      "http://www.domain4.com"
    };

    foreach (var url in urls)
    {
        // added await here
        await DownloadPageAsync(url);
    }
}

You need to create different tasks per every call you want, in your example you are running the code and not waiting for the call. 您需要为每个所需的呼叫创建不同的任务,在您的示例中,您正在运行代码而不等待呼叫。 When you call WhenAll that simply creates a task for all of them. 当您调用WhenAll时,仅会为所有这些任务创建一个任务。 Let's say that you use the code bellow and inside each MakeYouCall method you insert an item on a list. 假设您使用下面的代码,并且在每个MakeYouCall方法内部在列表中插入一个项目。 That list will be a shared resource that you need to lock. 该列表将是您需要锁定的共享资源。 When the WhenAll is made, then if you don't wait for the result(make a call to Wait() ) then the collection could be partially filled. 当创建WhenAll时,如果您不等待结果(调用Wait()),则可以部分填充集合。

var register1 = new Action(() => MakeYourCall1());
var register2 = new Action(() => MakeYourCall2());
var register3 = new Action(() => MakeYourCall3());

then 然后

var t1 = Task.Factory.StartNew(register1);
var t2 = Task.Factory.StartNew(register2);
var t3 = Task.Factory.StartNew(register3);

after that you can call the WhenAll that will return a Task, then wait for it. 之后,您可以调用WhenAll,它将返回一个Task,然后等待它。

Task.WhenAll(t1, t2, t3).Wait();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM