之前做后端脚本开发的时候,纠结了好久单线程和多线程哪个好,盲目跟风用多线程反而把项目搞崩了一次,踩了实打实的坑。那时候刚学线程开发,总觉得多线程听起来更高级、效率更高,不管什么任务都想套上多线程逻辑,完全没考虑实际的运行场景和硬件限制,最后折腾了整整一天才排查出问题根源。
当时接手的是一个简单的本地日志解析脚本,需求是批量读取本地几十份日志文件,筛选出异常报错数据,统一汇总生成统计表格。本身是纯磁盘读取、数据比对的轻量IO任务,逻辑简单、步骤固定,我却自作主张写了多线程并发逻辑,拆分了十个线程同时读取不同日志文件。
直接翻车。
运行脚本之后,电脑风扇疯狂转,CPU占用直接拉满,原本单线程十几秒就能跑完的任务,这次硬生生卡了五分钟还没结束,甚至出现了文件读取冲突的报错,部分日志数据重复读取、部分数据直接丢失,汇总的表格漏洞百出。
单线程和多线程的现场运行差异
为了搞清楚问题,当时逐行调试代码,还对照了Linux性能监控工具的实时数据,终于看清了两种线程模式在这个场景下的真实差距。我专门记录了同一批日志数据、同一设备环境下的运行参数,差异特别直观。
| 运行模式 | 任务耗时 | CPU占用 | 数据稳定性 |
|---|---|---|---|
| 单线程 | 12秒 | 20%左右 | 无数据错乱、无丢失 |
| 多线程(10线程) | 317秒 | 95%以上 | 数据重复、部分文件读取失败 |
后来才反应过来,这种频繁读写本地文件的IO密集型任务,根本不需要多线程。多线程的并发切换会产生大量的系统开销,线程之间频繁抢占资源、切换状态,反而会拖慢整体运行速度。而且本地磁盘的读写是串行资源,多个线程同时抢占读写权限,必然会出现资源竞争、数据冲突的问题,这也是数据出错的核心原因。
折腾好久才搞明白,多线程的优势只体现在CPU密集型任务里。比如批量数据运算、图像渲染、算法计算这类需要大量算力、几乎没有IO等待的任务,多线程拆分任务并行处理,能充分利用多核CPU性能,大幅缩短运行时间。
那段时间我又测试了数据排序运算任务,同样的上万组数据排序,单线程运行需要40多秒,开启四线程并行之后,耗时直接压缩到15秒左右,CPU利用率被充分利用,而且没有出现任何数据异常,运行状态特别稳定。
其实根本没有谁更好的说法,只有谁更适配当下的任务。IO等待多、流程简单、涉及资源抢占的任务,单线程足够好用,稳定、低开销、零报错;重度算力运算、无频繁IO操作的任务,多线程才能发挥价值,提升运行效率。
那天改完代码,把多线程逻辑删掉换回单线程,脚本秒级恢复正常运行,看着稳定的运行数据,只后悔一开始盲目跟风,白白浪费了大半天的调试时间。