【问题标题】:How to call an async method thousand of times in parallel如何并行调用数千次异步方法
【发布时间】:2019-12-24 01:26:33
【问题描述】:

我想并行调用一个异步方法大约 1000 次。

我有一个大约 10000 行的数据表。我想比较它的两行,如果某些条件为真,则调用异步方法。总的来说,这个方法(GetApiData())可能被调用了大约 1000 次。我读了几篇文章,写了一些代码。我的简化代码是:

public static HttpClientHandler httpClientHandler = new HttpClientHandler()
private static HttpClient Client = new HttpClient(handler: httpClientHandler, disposeHandler: true) { MaxResponseContentBufferSize = 10000000 };

private Button1_Click(Object sender, EventArgs e)
{
    httpClientHandler.Proxy = null;
    httpClientHandler.UseProxy = false;
    await DoWork();
}

public async Task DoWork()
{
    var allTasks = new List<Task>();
    for (int i = 0; i < dt.Rows.Count; i++)
    {
        if(Some condition)
        allTasks.Add(Task.Run(() => GetApiData()));            
    }
    Task waitAll = Task.WhenAll(allTasks);
    await waitAll;
}

public async Task GetApiData()
{
    var Result= await (await Client.GetAsync("Some Url")).Content.ReadAsStringAsync();
    //do some cpu bound(but not too heavy, like search in a datatable that have 20 rows) work on Result.
}

这段代码对我有用,但我认为它花费了太多时间。我有一些问题: 1-我的代码是并行的。如果不是我该怎么做。当我将断点设置为“Task waitAll = Task.WhenAll(allTask​​s);”时线,到达这条线需要太长时间,所以我认为我的代码不是并行的(我知道任务在创建后运行)。 2- 我需要在数据表中写入一些数据来改进我的代码,但我知道数据表不是线程安全的。有没有一种简单的方法可以将数据表转换为线程安全的数据结构。 3-我认为我的代码受 I/O 限制但是,如果我准备一台具有更多内核(如服务器)的计算机,例如 24 核和 48 线程,这是否会减少我的代码时间。谢谢。

【问题讨论】:

  • 我找不到它,但我很确定单个 HttpClient 存在硬性限制。但至于并行性,您的代码似乎是正确的。您启动所有查询并以正确的方式等待所有查询完成。 Datatable 是一个简单的结构。每行是一个类,每列有 1 个属性。使用自定义类创建线程友好的集合。对于测试,您可以使用 object[] 来包含这些值。是的,它是装箱/拆箱,但对于概念验证来说就足够了。准备好后,让它成为一个适当的类,你会很好去
  • @Franck,感谢您的回答。您的“单个 HttpClient 有硬性限制”的意思是什么。你会建议有几个 HttpClient 吗?如果我可以将我的数据表(或线程友好的集合)分成几个数据表并以并行模式运行每个数据表,这种情况是否可以减少我的代码时间。
  • 我不是 100% 确定,但我认为一旦你有 25 个查询出来,在至少有一个得到回应之前,没有其他人可以出去。不要把它当成事实,我只知道过去有这种限制,当我做很多查询时,解决方案就像拥有更多 HttpClient 对象一样简单。
  • @Franck 谢谢,您关于单个 HttpClient 硬限制的提示节省了我的时间。

标签: c# .net parallel-processing async-await parallel.foreach


【解决方案1】:

首先我感谢@Franck 的提示:“我找不到它,但我很确定单个 HttpClient 存在硬性限制”,我将数据表分为四个(我的系统有 4 个线程)单独的数据表然后定义四个 HttpClient 实例,每个 HttpClient 只用于数据表:

public static HttpClientHandler httpClientHandler = new HttpClientHandler()
private static HttpClient Client1 = new HttpClient(handler: httpClientHandler, disposeHandler: true) { MaxResponseContentBufferSize = 10000000 };`
private static HttpClient Client2 = new HttpClient(handler: httpClientHandler, disposeHandler: true) { MaxResponseContentBufferSize = 10000000 };`
private static HttpClient Client3 = new HttpClient(handler: httpClientHandler, disposeHandler: true) { MaxResponseContentBufferSize = 10000000 };`
private static HttpClient Client4 = new HttpClient(handler: httpClientHandler, disposeHandler: true) { MaxResponseContentBufferSize = 10000000 };

private Button1_Click(Object sender, EventArgs e)
{
    httpClientHandler.Proxy = null;
    httpClientHandler.UseProxy = false;
    Task t1=Task.Run(()=>DoWork(dt1,Client1));
    Task t1=Task.Run(()=>DoWork(dt2,Client2));
    Task t1=Task.Run(()=>DoWork(dt3,Client3));
    Task t1=Task.Run(()=>DoWork(dt4,Client4));
    await Task.WhenAll(t1,t2,t3,t4);
}

public async Task DoWork(DataTable dt,HttpClient Client)
{
    var allTasks = new List<Task>();
    for (int i = 0; i < dt.Rows.Count; i++)
    {
        if(Some condition)
        allTasks.Add(Task.Run(() => GetApiData(Client)));            
    }
    Task waitAll = Task.WhenAll(allTasks);
    await waitAll;
}

public async Task GetApiData(HttpClient Client)
{
    var Result= await (await Client.GetAsync("Some Url")).Content.ReadAsStringAsync();
    //do some cpu bound(but not too heavy, like search in a datatable that have 20 rows) work on Result.
}

在此之前,我的代码大约需要 450 秒,但现在大约需要 280 秒。我很惊讶,因为现在我认为我们必须在重用 HttpClient 和“对单个 HttpClient 的硬限制”之间取得平衡。谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-23
    • 1970-01-01
    • 2017-03-06
    • 2018-12-11
    • 2017-03-17
    • 1970-01-01
    相关资源
    最近更新 更多