You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么我编写的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 07:18:00