c#中的并行任务性能

Posted

技术标签:

【中文标题】c#中的并行任务性能【英文标题】:Parallel tasks performance in c# 【发布时间】:2020-09-09 10:18:34 【问题描述】:

我需要让任务运行得更快,我尝试使用信号量、并行库和线程(尝试为每项工作打开一个,我知道这是最愚蠢的做法),但它们都没有显示出我需要的性能.我不熟悉使用线程的东西,我需要一些帮助来找到正确的方法并了解任务和线程是如何工作的。

函数如下:

 public class Test
    
        public void openThreads()
        
            int maxConcurrency = 500;
            var someWork = get_data_from_database();
            using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
            
                List<Task> tasks = new List<Task>();
                foreach (var work in someWork)
                
                    concurrencySemaphore.Wait();

                    var t = Task.Factory.StartNew(() =>
                    
                        try
                        
                            ScrapThings(work);
                        
                        finally
                        
                            concurrencySemaphore.Release();
                        
                    );

                    tasks.Add(t);
                

                Task.WaitAll(tasks.ToArray());
            
        

        public async Task ScrapThings(Object work)
        
            HttpClient client = new HttpClient();
            Encoding utf8 = Encoding.UTF8;
            var response = client.GetAsync(work.url).Result;
            var buffer = response.Content.ReadAsByteArrayAsync().Result;
            string content = utf8.GetString(buffer);
            /*
             Do some parse operations, load html document, get xpath, split things, etc 
             */

            while(true) // this loop runs from 1~15 times
            
                response = client.GetAsync(work.anotherUrl).Result;
                buffer = response.Content.ReadAsByteArrayAsync().Result;
                content = utf8.GetString(buffer);
                if (content == "OK")
                    break;

                await Task.Delay(10000); //I need some throttle here before it tries again
            
            /*
                Do some parse operations, load html document, get xpath, split things, etc 
                */
            update_things_in_database();
        
    

我想让这个任务并行运行 500 次,所有操作需要 18 小时才能完成,我需要减少这个,我使用的是 32 核/64 线程的至强。我尝试打开 500 个线程(与信号量和并行库相比性能更好),但感觉不是正确的做法。

【问题讨论】:

您确定性能是受 CPU 限制而不是其他原因吗?使用ScrapThings 你有await Task.Delay(10000); //I need some throttle here before it tries again - 我假设这是因为你避免向服务器发送垃圾邮件并受到速率限制,但如果你有 500 个任务同时进行,那么延迟不是去上班,你会受到限制。 为什么async方法中有这么多像client.GetAsync(work.url).Result这样的阻塞调用? 听起来你需要设置连续线程数的生产者-消费者 @BrunoLeyne 但为什么不只是await client.GetAsync(work.url) 200 比 1 的几率是 您的 代码导致性能问题,并且您认为任务/线程/[选择您的毒药] 是原因,因为您从未分析过应用程序。 【参考方案1】:

我想说性能问题不在于您如何运行线程,而在于各个线程的执行方式。取决于您使用的 .NET/库版本,可能存在的问题很少。

    您应该重用HttpClient 实例,原因例如here。 如果work.urlwork.anotherUrl 使用相同的域子集,您应该查看每个端点的连接限制(以及总数)。取决于版本HttpClientHandler.MaxConnectionsPerServer 或ServicePoint.ConnectionLimit 和ServicePointManager.DefaultConnectionLimit 。前者用于 .NET Core,后者用于.NET Full framework。

解决第一个问题的推荐方法是使用IHttpClientFactory

还有更多info。

UPD

您在 cmets 中提到您正在使用 .NET 4.7.2,因此我建议您首先在您的应用程序中添加下一行(在开头的某个位置):

ServicePointManager.DefaultConnectionLimit = 500;
// if you can get collection of most scrapped ones:
var domains = new []  "http://slowwly.robertomurray.co.uk" ;
foreach(var d in domains)

    var delayServicePoint = ServicePointManager.FindServicePoint(new Uri(d));
    delayServicePoint.ConnectionLimit = 10; // or bigger

【讨论】:

由于 cookie/会话管理,我无法重用 HttpClient。我有 86 个不同的 ScrapThings 函数,因为我需要废弃不同的东西。我从未使用过 IHttpClientFactory,我将阅读文档以查看有什么区别。我会花一些时间阅读。 1) 查看更新 2) 如果您使用工厂或只是 HttpClient 的池,则可以。 感谢您提供的所有信息,现在我正在阅读您发布的所有文档,以了解如何更好地管理 http 连接并尝试新的方法。我需要一些时间来处理所有事情。【参考方案2】:

这听起来像是TPL Dataflow 库的工作。对于 I/O 绑定操作(Web 请求、数据库更新)和 CPU 绑定操作(数据解析),您可能需要不同的并发级别。 TPL 数据流允许构建一个管道,其中每个块负责一个操作,数据从一个块流向下一个块。它甚至允许循环图,例如,您可以将失败的数据元素扔回块中,以便再次处理它。

有关使用此库的一些示例,请查看 here、here 或 here。

TPL 数据流库嵌入在 .NET Core 中,可用作 .NET Framework 的 package。

【讨论】:

以上是关于c#中的并行任务性能的主要内容,如果未能解决你的问题,请参考以下文章

C# 并行和多线程编程——认识和使用Task

C# 并行与。线程代码性能

在 C# .NET 5 中同时并行执行任务

如何使并行独立任务在 C# 中调用 WCF

如何等待并行任务完成

c#数据流或任务,消费消息并行处理