如何让异步代码等待所有任务完成?
How do I get async code to wait until all tasks are complete?
当我 运行 下面的代码时,消息 "Press Enter to continue... "
在 HttpClient.GetAsync()
调用完成返回结果之前出现。事件的实际顺序:进行 GetAsync()
调用,出现“Press Enter...
”消息,然后将结果一一输出到控制台 window。如何在显示“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();
}
}
由于DownloadPageAsync
returns一个任务,你可以列出所有任务并等待它们:
Task.WhenAll(urls.Select(url => DownloadPageAsync(url)))
或简化:
Task.WhenAll(urls.Select(DownloadPageAsync))
我认为问题在于您没有在 RunAsync()
方法中等待 DownloadPageAsync
方法。如果您将 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);
}
}
您需要为每次调用创建不同的任务,在您的示例中,您是 运行 代码而不是等待调用。当您调用 WhenAll 时,它只是为所有这些创建一个任务。假设您使用下面的代码,并在每个 MakeYouCall 方法中向列表中插入一个项目。该列表将是您需要锁定的共享资源。完成 WhenAll 后,如果您不等待结果(调用 Wait() ),则可能会部分填充集合。
var register1 = new Action(() => MakeYourCall1());
var register2 = new Action(() => MakeYourCall2());
var register3 = new Action(() => MakeYourCall3());
然后
var t1 = Task.Factory.StartNew(register1);
var t2 = Task.Factory.StartNew(register2);
var t3 = Task.Factory.StartNew(register3);
之后您可以调用将 return 任务的 WhenAll,然后等待它。
Task.WhenAll(t1, t2, t3).Wait();
@patrik-hofman 的回答很好(投了赞成票),不过请参阅我的评论
如果您希望请求按顺序发生...
将 await
添加到 DownloadPageAsync
行。
您在 RunAsync
中使用了 async
,但没有 await
。因此,尽管它 returns 是一个任务,但它并不等待 DownloadPageAsync
调用完成。这意味着该方法只是 returns 一个立即完成的 "empty" 任务。所以你的 .Wait()
什么都没等。
当我 运行 下面的代码时,消息 "Press Enter to continue... "
在 HttpClient.GetAsync()
调用完成返回结果之前出现。事件的实际顺序:进行 GetAsync()
调用,出现“Press Enter...
”消息,然后将结果一一输出到控制台 window。如何在显示“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();
}
}
由于DownloadPageAsync
returns一个任务,你可以列出所有任务并等待它们:
Task.WhenAll(urls.Select(url => DownloadPageAsync(url)))
或简化:
Task.WhenAll(urls.Select(DownloadPageAsync))
我认为问题在于您没有在 RunAsync()
方法中等待 DownloadPageAsync
方法。如果您将 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);
}
}
您需要为每次调用创建不同的任务,在您的示例中,您是 运行 代码而不是等待调用。当您调用 WhenAll 时,它只是为所有这些创建一个任务。假设您使用下面的代码,并在每个 MakeYouCall 方法中向列表中插入一个项目。该列表将是您需要锁定的共享资源。完成 WhenAll 后,如果您不等待结果(调用 Wait() ),则可能会部分填充集合。
var register1 = new Action(() => MakeYourCall1());
var register2 = new Action(() => MakeYourCall2());
var register3 = new Action(() => MakeYourCall3());
然后
var t1 = Task.Factory.StartNew(register1);
var t2 = Task.Factory.StartNew(register2);
var t3 = Task.Factory.StartNew(register3);
之后您可以调用将 return 任务的 WhenAll,然后等待它。
Task.WhenAll(t1, t2, t3).Wait();
@patrik-hofman 的回答很好(投了赞成票),不过请参阅我的评论
如果您希望请求按顺序发生...
将 await
添加到 DownloadPageAsync
行。
您在 RunAsync
中使用了 async
,但没有 await
。因此,尽管它 returns 是一个任务,但它并不等待 DownloadPageAsync
调用完成。这意味着该方法只是 returns 一个立即完成的 "empty" 任务。所以你的 .Wait()
什么都没等。