【问题标题】:Multithreading/Concurrent strategy for a network based task基于网络的任务的多线程/并发策略
【发布时间】:2018-04-05 22:20:55
【问题描述】:

我不擅长充分利用资源,因此我正在为需要并行且高效完成的任务寻找最佳方式。

我们有一个场景,我们必须 ping 数百万个系统并接收响应。响应本身不需要计算时间,但任务是基于网络的。

我当前的实现是这样的 -

Parallel.ForEach(list, ip =>
{
    try
    {
        // var record = client.QueryAsync(ip);
        var record = client.Query(ip);
        results.Add(record);
    }
    catch (Exception)
    {
        failed.Add(ip);
    }
});

我测试了这段代码

  • 100 件商品大约需要 4 秒
  • 1k 个项目大约需要 10 秒
  • 10k 个项目大约需要 80 秒
  • 100k 个项目大约需要 710 秒

我需要处理接近 20M 的查询,我应该使用什么策略来进一步加快速度

【问题讨论】:

  • 警告:results(和failed)如果是List<T>,则不是线程安全的。
  • 是的,我使用 concurrentBag 但这只是加速的绝望措施,我将恢复为线程安全集合
  • 如果 client.Query 有异步版本,那么最好的方法是使用它,因为网络调用是 IO 任务。
  • 这里列出了多个选项(数据流、自定义分区器、信号量苗条):stackoverflow.com/q/14673728/5311735。您需要使用QueryAsync 和一定程度的并行性,您应该以经验方式找到它们,例如 100,甚至可能是 1000。无限程度可能会或可能不会使您的套接字饱和,这取决于 QueryAsync 完成的速度和一些其他的东西,所以也值得尝试。请注意,只有当QueryAsync 使用真正的异步 IO 而不是伪造它(通过Task.Run 之类的东西)时,它才会起作用。
  • 如果发生这种情况 QueryAsync 不是真正的异步 - 请执行 ThreadPool.SetMinThreads(100, 8)(其中 100 是您需要的并行度)并再次尝试任何解决方案。

标签: c# .net multithreading


【解决方案1】:

问题来了

Parallel.ForEach 使用线程池。此外,IO 绑定 操作会阻塞那些等待设备响应的线程并占用资源。

  • 如果您有CPU 绑定 代码,并行度 是合适的;
  • 虽然如果您有IO 绑定 代码,Asynchrony 是合适的。

在这种情况下,client.Query 显然是 I/O,因此理想的消费代码应该是异步的。

既然你说有一个异步版本,你最好使用async/await 模式和/或对并发任务的某种类型的限制,另一个巧妙的解决方案是在TPL dataflow 中使用ActionBlock Class图书馆。


数据流示例

public static async Task DoWorkLoads(List<IPAddress> addresses)
{
   var options = new ExecutionDataflowBlockOptions
                     {
                        MaxDegreeOfParallelism = 50
                     };

   var block = new ActionBlock<IPAddress>(MyMethodAsync, options);

   foreach (var ip in addresses)
      block.Post(ip);

   block.Complete();
   await block.Completion;

}

...

public async Task MyMethodAsync(IpAddress ip)
{

    try
    {
        var record = await client.Query(ip);
        // note this is not thread safe best to lock it
        results.Add(record);
    }
    catch (Exception)
    {
        // note this is not thread safe best to lock it
        failed.Add(ip);
    }
}

这种方式给你异步,也给你MaxDegreeOfParallelism,不浪费资源,让IO成为IO无需消耗不必要的资源

*免责声明,DataFlow 可能不是您想要的,但我只是认为 id 可以为您提供更多信息


Demo here

更新

我刚刚用 Parallel.Foreaceh 和 DataFlow 做了一些基准测试

运行多次 10000 ping

Parallel.Foreach = 30 秒

数据流 = 10 秒

【讨论】:

  • 谢谢,会试试这个并带回数据,谢谢你给我指出新的东西! (对我来说是新的)
  • 我无法正确处理流程,代码不会在 block.Completion 处等待
  • @Muds 2 secs 我还没有测试过,我现在就检查一下
  • 另外,是否会使用 parallel.foreach 来提高性能?
  • @Muds 是的,它会,开销更少,你需要根据自己的情况解决这个问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-21
  • 2020-01-19
  • 1970-01-01
  • 2019-10-30
  • 1970-01-01
  • 2021-04-28
  • 2021-09-14
相关资源
最近更新 更多