c# - 通过异步示例了解 C# 中的并行编程
问题描述
我正在尝试理解并行编程,并且我希望我的async
方法可以在多个线程上运行。我已经写了一些东西,但它并没有像我想象的那样工作。
代码
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
. 虽然async void
方法是有效的,但它们通常会在新线程上继续工作,并在它们await
成为任务后立即返回给调用者,Parallel.ForEach
因此该方法认为处理程序已完成,即使它已将剩余的工作踢到单独的线程中。
相反,在这里使用同步方法;
Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, index =>
{
var response = client.GetAsync("posts/" + index).Result;
var contents = response.Content.ReadAsStringAsync().Result;
listResults.Add(contents);
Console.WriteLine(contents);
});
如果您绝对必须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
下去;
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
期望 a Func<T, TResult>
,它会将async
带有 no 的 lambda解释return
为async Task
方法而不是async void
,从而为我们提供一些我们可以显式await
推荐阅读
- python - 在 tkinter 按钮中调用一个变量,它也运行一个函数
- dart - 带有嵌套 AnimatedList 的 Firestore StreamBuilder
- java - 如何根据所选日期更改我的列表数据?
- angular - 以角度创建第一个项目时出错
- python - 在 Python 中存储和比较多个属性
- coldfusion - ColdBox 视图查询输出错误“无法转换复杂对象类型查询:字符串”
- reactjs - (反应)有没有办法在不触发未定义的情况下附加变量的值
- java - 在处理中添加二维数组行
- apache-poi - apache poi中的rowspan和colspan
- java - 用 JavaFX 画一条直线