了解带有异步示例的C#并行编程

问题描述 投票:2回答:1

我试图理解并行编程,我希望异步方法可以在多个线程上运行。我已经写了一些东西,但是它没有按照我认为的那样工作。

代码

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方法在多个线程上运行。

任何帮助将不胜感激!

c# parallel-processing task-parallel-library parallel.foreach
1个回答
0
投票

这里发生的事情是,您永远不必等待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]

© www.soinside.com 2019 - 2024. All rights reserved.