我试图理解并行编程,我希望异步方法可以在多个线程上运行。我已经写了一些东西,但是它没有按照我认为的那样工作。
代码
public static async Task Main(string[] args)
{
var listAfterParallel = RunParallel(); // Running this function to return tasks
await Task.WhenAll(listAfterParallel); // I want the program exceution to stop until all tasks are returned or tasks are completed
Console.WriteLine("After Parallel Loop"); // But currently when I run program, after parallel loop command is printed first
Console.ReadLine();
}
public static async Task<ConcurrentBag<string>> RunParallel()
{
var client = new System.Net.Http.HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.BaseAddress = new Uri("https://jsonplaceholder.typicode.com");
var list = new List<int>();
var listResults = new ConcurrentBag<string>();
for (int i = 1; i < 5; i++)
{
list.Add(i);
}
// Parallel for each branch to run await commands on multiple threads.
Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, async (index) =>
{
var response = await client.GetAsync("posts/" + index);
var contents = await response.Content.ReadAsStringAsync();
listResults.Add(contents);
Console.WriteLine(contents);
});
return listResults;
}
我希望RunParallel函数在打印“并行循环之后”之前完成。我也想让我的get posts方法在多个线程上运行。
任何帮助将不胜感激!
这里发生的事情是,您永远不必等待Parallel.ForEach
块完成-您只是将最终返回的袋子放回去。这样做的原因是因为Parallel.ForEach
需要Action
委托,所以您创建了一个返回void
而不是Task
的lambda。尽管async void
方法有效,但是它们通常在新线程上继续工作,并在他们await
一个Task后立即返回调用者,因此Parallel.ForEach
方法认为处理程序已经完成,即使被踢了剩下的工作放到一个单独的线程中。
相反,请在此处使用同步方法。如果绝对必须在其中使用await
,则将其包装在Task.Run(...).GetAwaiter().GetResult()
;
Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, index => Task.Run(async () =>
{
var response = await client.GetAsync("posts/" + index);
var contents = await response.Content.ReadAsStringAsync();
listResults.Add(contents);
Console.WriteLine(contents);
}).GetAwaiter().GetResult();
但是,在这种情况下,Task.run
通常转到新线程,因此我们颠覆了Parallel.ForEach的大部分控制。如果改为取消async
处理程序,则在读取结果时会损失async
的任何收益。因此,最好做一个或另一个;
var tasks = list.Select(async (index) => {
var response = await client.GetAsync("posts/" + index);
var contents = await response.Content.ReadAsStringAsync();
listResults.Add(contents);
Console.WriteLine(contents);
});
await Task.WhenAll(tasks);
由于Select
期望Select
,它将把没有Func<T, TResult>
的async
lambda解释为return
方法,而不是async Task
,因此我们可以明确地async void
]