C# 如何让异步代码等待所有任务完成?
当我运行下面的代码时,在从C# 如何让异步代码等待所有任务完成?,c#,asynchronous,.net-4.5,C#,Asynchronous,.net 4.5,当我运行下面的代码时,在从HttpClient.GetAsync()调用返回结果之前,会出现消息“按Enter键继续…”。事件的实际顺序:调用GetAsync(),出现“按Enter…”消息,然后将结果逐个输出到控制台窗口。如何等到所有的GetAsync()调用完成后再显示“按Enter…”消息 类程序 { 静态HttpClient=新HttpClient(); 静态void Main(字符串[]参数) { RunAsync().Wait(); Console.WriteLine(“\n\n\n
HttpClient.GetAsync()
调用返回结果之前,会出现消息“按Enter键继续…”
。事件的实际顺序:调用GetAsync()
,出现“按Enter…
”消息,然后将结果逐个输出到控制台窗口。如何等到所有的GetAsync()
调用完成后再显示“按Enter…
”消息
类程序
{
静态HttpClient=新HttpClient();
静态void Main(字符串[]参数)
{
RunAsync().Wait();
Console.WriteLine(“\n\n\n\n按Enter继续…”);
Console.ReadLine();
}
静态异步任务RunAsync()
{
列表URL=新列表()
{
"http://www.domain1.com",
"http://www.domain2.com",
"http://www.domain3.com",
"http://www.domain4.com"
};
foreach(url中的变量url)
{
下载页面异步(url);
}
}
静态异步任务下载页面异步(字符串url)
{
Console.WriteLine(“起始:+url”);
HttpResponseMessage response=wait client.GetAsync(url);
if(响应。IsSuccessStatusCode)
{
//在这里做事
}
Console.WriteLine(“完成:+url”);
返回response.Content.ToString();
}
}
由于DownloadPageAsync
返回一个任务,您可以列出所有任务并等待它们:
Task.WhenAll(urls.Select(url => DownloadPageAsync(url)))
或简化:
Task.WhenAll(urls.Select(DownloadPageAsync))
由于
DownloadPageAsync
返回一个任务,您可以列出所有任务并等待它们:
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);
}
}
静态异步任务RunAsync()
{
列表URL=新列表()
{
"http://www.domain1.com",
"http://www.domain2.com",
"http://www.domain3.com",
"http://www.domain4.com"
};
foreach(url中的变量url)
{
//在这里添加等待
等待下载页面异步(url);
}
}
我认为问题在于您没有在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);
}
}
静态异步任务RunAsync()
{
列表URL=新列表()
{
"http://www.domain1.com",
"http://www.domain2.com",
"http://www.domain3.com",
"http://www.domain4.com"
};
foreach(url中的变量url)
{
//在这里添加等待
等待下载页面异步(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);
之后,您可以调用WhenAll返回任务,然后等待它
Task.WhenAll(t1, t2, t3).Wait();
您需要为每个调用创建不同的任务,在您的示例中,您正在运行代码,而不是等待调用。当您调用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);
之后,您可以调用WhenAll返回任务,然后等待它
Task.WhenAll(t1, t2, t3).Wait();
@patrik hofman的答案是一个很好的答案(投票率上升),不过,请看我的评论 如果您希望请求按顺序进行。。。 将
wait
添加到DownloadPageAsync
行
您在
RunAsync
中使用了async
,但没有wait
s。因此,尽管它返回一个任务,但它并不等待DownloadPageAsync
调用完成。这意味着该方法只返回一个立即完成的“空”任务。所以你的.Wait()
什么也不等。帕特里克·霍夫曼的答案很好(得票较高),不过,请看我的评论
如果您希望请求按顺序进行。。。
将wait
添加到DownloadPageAsync
行
您在
RunAsync
中使用了async
,但没有wait
s。因此,尽管它返回一个任务,但它并不等待DownloadPageAsync
调用完成。这意味着该方法只返回一个立即完成的“空”任务。所以你的.Wait()
什么也不等。什么机制可以让你把它简化成任务。什么时候(URL.Select(DownloadPageAsync))
?这是我第一次遇到这种格式。Select
调用Func
,而DownloadPageAsync
恰好就是这种格式。请确保从RunAsync
中删除async
关键字,并返回任务。whalll(…)
还请注意,这对于少量URL是可以的。如果你计划在100或1000年(如爬行器/爬虫)使用,那么你需要一种更复杂的方法来限制并发。我考虑的更多的是使用MaxConcurrency
设置的Parallel.ForEach
或自定义TaskScheduler
。可能使用BlockingCollection
作为输入队列和getconsumineGenumerable
。比如什么机制允许