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

C#:File.ReadLines多线程并行执行时线程分配异常求助

解决多线程下使用File.ReadLines时的线程资源不均衡问题

咱们先把问题根源理清楚:你遇到的"线程轮流霸占资源、其余线程闲置"的情况,本质是因为File.ReadLines返回的是延迟加载的单线程枚举器,它本身不是线程安全的。当多个任务线程同时去抢这个枚举器的控制权时,就会出现某个线程一次性拿到大量行数据处理,其他线程只能等待,等这个线程处理完一批,下一个线程才会抢到资源,循环往复,自然没法让你那10个线程都充分参与工作。

下面给你几个能彻底解决问题的方案,按需选择:

方案1:预先拆分文件块,让任务独立处理

核心思路是把整个文件拆成若干个独立的块(比如对应你要跑的4个任务),每个任务单独读取自己负责的块范围,完全避免线程间的资源竞争。

var fileInfo = new FileInfo("你的文件路径.txt");
// 按任务数拆分块大小,这里假设跑4个任务
long blockSize = fileInfo.Length / 4;
var taskList = new List<Task>();

for (int i = 0; i < 4; i++)
{
    int taskIdx = i;
    taskList.Add(Task.Run(() =>
    {
        long startPos = taskIdx * blockSize;
        // 最后一个块要包含剩余所有内容
        long endPos = taskIdx == 3 ? fileInfo.Length : (taskIdx + 1) * blockSize - 1;

        using (var reader = new StreamReader(File.OpenRead("你的文件路径.txt")))
        {
            // 定位到当前块的起始位置
            reader.BaseStream.Seek(startPos, SeekOrigin.Begin);
            
            // 处理边界:如果起始位置不是行首,跳过当前不完整的行
            if (startPos > 0)
            {
                reader.ReadLine();
            }

            // 读取到块的结束位置
            string line;
            while ((line = reader.ReadLine()) != null && reader.BaseStream.Position <= endPos)
            {
                // 替换成你的业务处理逻辑
                ProcessSingleLine(line);
            }
        }
    }));
}

await Task.WhenAll(taskList);

这个方案的优势是每个任务完全独立,不会互相干扰,能充分调动所有可用线程(包括你说的10个),而且性能稳定。需要注意的是一定要处理行边界,避免把一行拆成两个块。

方案2:生产者-消费者模式,统一分发任务

如果不想预先拆分文件,可以用一个生产者线程专门读取所有行,把行数据放到线程安全的队列里,再启动10个消费者线程从队列里取任务处理,保证所有线程都忙起来。

// 线程安全的阻塞队列,自动处理线程同步
var lineQueue = new BlockingCollection<string>(new ConcurrentQueue<string>());

// 生产者线程:负责读取所有行到队列
var producerTask = Task.Run(() =>
{
    foreach (var line in File.ReadLines("你的文件路径.txt"))
    {
        lineQueue.Add(line);
    }
    // 标记队列已完成添加,消费者线程会知道什么时候停止
    lineQueue.CompleteAdding();
});

// 消费者线程:启动10个线程处理队列中的行
var consumerTasks = Enumerable.Range(0, 10).Select(_ => Task.Run(() =>
{
    foreach (var line in lineQueue.GetConsumingEnumerable())
    {
        // 替换成你的业务处理逻辑
        ProcessSingleLine(line);
    }
})).ToList();

// 等待生产者和所有消费者任务完成
await Task.WhenAll(producerTask, Task.WhenAll(consumerTasks));

这个模式不用关心文件拆分的细节,BlockingCollection会自动帮你处理线程间的同步,10个消费者线程会持续从队列取任务,不会出现闲置的情况。

方案3:用Partitioner配合Parallel.ForEach均衡分配任务

如果想简化代码,可以用System.Collections.Concurrent.Partitioner来帮你拆分File.ReadLines的枚举流,让Parallel.ForEach能更均衡地把任务分配到10个线程上。

var allLines = File.ReadLines("你的文件路径.txt");
// 创建无缓冲的分区器,避免某个线程一次性拿到大量行
var partitioner = Partitioner.Create(allLines, EnumerablePartitionerOptions.NoBuffering);

// 限制最大并行数为10,让所有线程都参与
Parallel.ForEach(partitioner, new ParallelOptions { MaxDegreeOfParallelism = 10 }, line =>
{
    // 替换成你的业务处理逻辑
    ProcessSingleLine(line);
});

这里的EnumerablePartitionerOptions.NoBuffering是关键,它会阻止分区器预先抓取大量行数据,而是按需分配给空闲的线程,保证10个线程都能均匀分到工作,不会出现某几个线程霸占资源的情况。


内容的提问来源于stack exchange,提问作者user8549339

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:00:24