Bare metal C#无限循环中运行线程不耗尽内存与线程资源的方法
问题根源
你原来的写法每次Update调用都会为每个遍历项创建全新线程,线程创建、销毁的开销极高,且裸机环境没有操作系统的线程过载调度保护,线程数超过硬件核心数后,上下文切换开销会直接占满所有硬件资源,最终导致进程崩溃。
解决方案
方案1:自定义固定大小线程池(最适配裸机场景)
提前创建和CPU核心数一致的工作线程复用,避免每次循环新建线程。裸机环境下没有复杂的系统线程池调度,这个方案开销最低:
// 程序初始化阶段提前创建线程池,线程数和硬件核心数保持一致 ConcurrentQueue<Action> taskQueue = new ConcurrentQueue<Action>(); int coreCount = Environment.ProcessorCount; Thread[] workerThreads = new Thread[coreCount]; for (int i = 0; i < coreCount; i++) { workerThreads[i] = new Thread(() => { while (true) { if (taskQueue.TryDequeue(out Action task)) { task.Invoke(); } else { // 裸机环境可替换为更低开销的闲置等待逻辑 Thread.Sleep(0); } } }); workerThreads[i].IsBackground = true; workerThreads[i].Start(); }
Update方法中的循环只需将任务推入队列即可:
for (int i = 0; i < Apps.Count; i++) { int index = i; // 规避闭包捕获索引的坑 taskQueue.Enqueue(() => /* 你的耗时方法逻辑 */); }
如果你的裸机运行时没有内置
ConcurrentQueue,可以自行实现带锁的普通Queue,开销远低于新建线程。
方案2:分帧分批执行(无多线程开销,最稳定)
如果耗时方法不需要实时完成,可以把遍历任务拆分到多帧执行,完全不需要多线程,就能保证帧率稳定:
const int BatchPerFrame = 2; // 可根据实际帧率动态调整,帧率高就加,低就减 int currentOffset = 0; void Update() { int endIndex = Math.Min(currentOffset + BatchPerFrame, Apps.Count); for (int i = currentOffset; i < endIndex; i++) { /* 你的耗时方法逻辑 */ } // 遍历完一轮就重置偏移量 currentOffset = endIndex >= Apps.Count ? 0 : endIndex; }
方案3:限流并行遍历(仅支持Parallel类的运行时可用)
如果你的裸机C#运行时支持Parallel类,可以直接指定最大并行度,内置逻辑会自动复用线程:
Parallel.For(0, Apps.Count, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount // 限制并行数不超过核心数 }, i => { /* 你的耗时方法逻辑 */ });
注意事项
- 多线程场景下如果涉及修改
Apps列表或其他共享变量,必须加锁,避免裸机环境出现未定义行为 - 线程/并行数不要超过硬件CPU核心数,裸机没有操作系统的智能调度,超核心数的线程只会增加额外开销,反而降低性能
内容的提问来源于stack exchange,提问作者Eli Ozcan
相关产品推荐
相关产品推荐

