为什么我编写的C#多线程程序执行速度比串行程序更慢?
多线程代码性能低于串行方案的核心问题
1. 细粒度锁导致极高的锁竞争开销
你当前实现中每入队1条、出队1条记录都要单独加一次锁,10亿条记录会产生至少20亿次锁申请/释放操作,大量CPU资源浪费在锁的上下文切换、竞争等待上,完全抵消了多线程并行的收益。而你的串行方案是批处理,没有锁开销,性能自然更高。
2. 消费线程空转浪费CPU资源
ProcessData方法中队列空且文件未读完时直接执行continue,会导致消费线程持续循环抢锁、不做任何有效操作,不仅占用大量CPU,还会进一步加剧锁竞争,拖慢生产线程的执行效率。
3. 计时逻辑完全错误
Main方法中启动两个线程后立刻结束计时,你打印的时间只是线程创建、启动的耗时,根本不是整个文件处理的实际耗时。需要在启动线程后调用thread1.Join()、thread2.Join()等待两个线程执行完成,再结束计时才能得到正确的耗时数据。
4. 多线程变量可见性风险
fileread字段没有加volatile修饰,在CPU缓存一致性的影响下,消费线程可能感知不到生产线程对fileread的修改,极端情况下会出现消费线程无限空转的问题。
5. 无谓的线程等待开销
生产线程中队列满后执行Thread.Sleep(1),这个等待逻辑没有必要,会拖慢队列填充的速度,进一步降低整体处理效率。
优化方案
推荐直接使用.NET 内置的生产者消费者专用集合BlockingCollection<T>,它自带线程安全控制、阻塞等待、容量上限控制能力,不需要自己手写锁和同步逻辑,优化后核心逻辑示例如下:
// 初始化集合,设置最大容量为100万条 BlockingCollection<string> logQueue = new BlockingCollection<string>(1000000); // 生产者逻辑 void PopulateQueue(string filePath) { foreach (var line in File.ReadLines(filePath)) { // 队列满时会自动阻塞,不需要自己判断大小和Sleep logQueue.Add(line); } // 标记生产完成 logQueue.CompleteAdding(); } // 消费者逻辑 void ProcessData() { // 队列空且生产未完成时会自动阻塞,不需要自己写空转逻辑 foreach (var msg in logQueue.GetConsumingEnumerable()) { // 处理消息逻辑 } } // Main逻辑 static void Main(string[] args) { DateTime startTime = DateTime.Now; var logQueue = new BlockingCollection<string>(1000000); var producer = new Thread(() => PopulateQueue("你的文件路径")); var consumer = new Thread(ProcessData); producer.Start(); consumer.Start(); // 等待两个线程执行完成再计时 producer.Join(); consumer.Join(); DateTime endTime = DateTime.Now; Console.WriteLine($"总耗时:{endTime.Subtract(startTime).TotalMilliseconds}ms"); }
优化后可以完全避免锁竞争、空转等问题,充分发挥多线程并行读写的优势,性能会远高于你的串行实现。
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

