在.net Core,C#中并行校准API调用的最佳方法是什么?

Lea*_*Net 2 c# parallel-processing task-parallel-library asp.net-core

我想并行调用我的API次数,以便可以快速完成处理。我下面有三种方法必须并行调用API。我试图了解哪种是执行此操作的最佳方法。

基本代码

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 List<string>();
for (int i = 1; i < 5; i++)
{
    list.Add(i);
}
Run Code Online (Sandbox Code Playgroud)

使用Parallel.ForEach的第一种方法

Parallel.ForEach(list,new ParallelOptions() { MaxDegreeOfParallelism = 3 }, index =>
{
    var response = client.GetAsync("posts/" + index).Result;

    var contents =  response.Content.ReadAsStringAsync().Result;
    listResults.Add(contents);
    Console.WriteLine(contents);
});

Console.WriteLine("After all parallel tasks are done with Parallel for each");
Run Code Online (Sandbox Code Playgroud)

带有任务的第二种方法。我不确定这是否并行。让我知道是否可以

var loadPosts = new List<Task<string>>();
foreach(var post in list)
{
    var response = await client.GetAsync("posts/" + post);

    var contents = response.Content.ReadAsStringAsync();
    loadPosts.Add(contents);
    Console.WriteLine(contents.Result);
}

await Task.WhenAll(loadPosts);

Console.WriteLine("After all parallel tasks are done with Task When All");
Run Code Online (Sandbox Code Playgroud)

使用动作阻止的第三种方法-这是我应该一直做的事情,但我希望听到社区的意见

var responses = new List<string>();

var block = new ActionBlock<int>(
    async x => {
        var response = await client.GetAsync("posts/" + x);
        var contents = await response.Content.ReadAsStringAsync();
        Console.WriteLine(contents);
        responses.Add(contents);                
    },
    new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = 6, // Parallelize on all cores
    });

for (int i = 1; i < 5; i++)
{
    block.Post(i);
}

block.Complete();
await block.Completion;

Console.WriteLine("After all parallel tasks are done with Action block");
Run Code Online (Sandbox Code Playgroud)

Ted*_*d M 6

我制作了一个小控制台应用程序来测试 ping API“https://jsonplaceholder.typicode.com/todos/{i}” 200 次的所有方法。@MikeLimaSierra 方法 1 或 3 是最快的!

方法 并行度 时间
不平行 不适用 8.4秒
@LearnAspNet(OP)方法1 2 5.494秒
@LearnAspNet(OP)方法1 30 1.235秒
@LearnAspNet(OP)方法3 2 4.750秒
@LearnAspNet(OP)方法3 30 1.795秒
@jamespconnor方法 不适用 21.5秒
@YuliBonner方法 不适用 21.4秒


Yul*_*ner 5

方法2接近。这是一条经验法则:I / O绑定操作=>使用Tasks / WhenAll(异步),计算绑定操作=>使用并行性。Http请求是网络I / O。

            foreach (var post in list)
            {
                async Task<string> func()
                {
                    var response = await client.GetAsync("posts/" + post);
                    return await response.Content.ReadAsStringAsync();
                }

                tasks.Add(func());
            }

            await Task.WhenAll(tasks);

            var postResponses = new List<string>();

            foreach (var t in tasks) {
                var postResponse = await t; //t.Result would be okay too.
                postResponses.Add(postResponse);
                Console.WriteLine(postResponse);
            }
Run Code Online (Sandbox Code Playgroud)

  • 请注意,func() 调用前面没有等待。所以这段代码不会等待第一个 for 循环内的任何任务。本地函数正在返回一个任务。任务集合是可能完整也可能不完整的任务的集合。await WhenAll 创建一个延续,该延续在任务中的所有 Task 对象完成后执行。Async/await/WhenAll 是关于异步而不是并行。您的困惑可能与本地功能有关。async Task&lt;string&gt; func() 是函数定义,而不是函数调用。 (4认同)