FIFO在并发控制中实现硬限流:通过阻塞式打开和读写语义,精确限制最多N个任务同时运行。其核心是利用内核级同步通道特性,无需轮询或额外工具,仅靠系统调用与Shell重定向即可达成无竞态、自动回收的并发调度。

理解FIFO在并发控制中的核心作用
命名管道(FIFO)不是普通文件,而是一个内核级的同步通道。它天然具备“阻塞等待读端”的特性:当一个进程以O_WRONLY打开FIFO时,若此时没有进程以O_RDONLY打开它,该打开操作就会阻塞——这个行为正是实现**并发数硬限流**的关键机制。不需要轮询、不需要信号量、不依赖外部工具,仅靠系统调用和Shell的exec重定向就能达成精确的“最多N个任务同时运行”。
构建限速器的三步骨架
整个限速器由三部分构成:一个全局FIFO作为“许可证池”,一组后台任务进程,以及一个中央协调逻辑。关键不在“启动多少”,而在“卡住多少”。
-
创建带N个令牌的FIFO池:用
mkfifo建一个管道,再向其中写入N个换行符(每个换行符代表一个可用并发槽位) -
用
exec绑定文件描述符到FIFO:每个任务启动前,先以读写模式()打开FIFO并绑定到一个自定义fd(如fd 3),这样一次读操作就消耗一个槽位 - 任务执行完后自动归还槽位:子shell退出时,其绑定的fd自动关闭,FIFO另一端感知到写端关闭,触发内核释放该连接——但真正让新任务能抢到槽位的,是“读端关闭后,下一个等待的写端才能成功打开”这一语义
可直接运行的企业级脚本模板
以下是一个生产环境可用的并发控制器,支持指定最大并发数、任务列表、超时保护和错误捕获:
#!/bin/bash
# usage: ./concurrent.sh 5 task1.sh task2.sh ...
MAX_CONCURRENCY=$1; shift
FIFO="/tmp/conc_$$"
<h1>创建FIFO并预置N个令牌(每行一个)</h1><p>mkfifo "$FIFO"
for ((i=0; i<max_concurrency i do echo> "$FIFO"
done</max_concurrency></p><h1>启动每个任务:先取令牌,再执行,退出即释放</h1><p>for task in "$@"; do
(</p><h1>阻塞式获取一个槽位(读一行)</h1><pre class="brush:php;toolbar:false;">read -u 3 _ || exit 1
# 执行实际任务(加超时防死锁)
timeout 300 "$task" &> "/tmp/log_$$_${task##*/}.log") 3
等待全部完成
wait
清理
rm -f "$FIFO"
说明:3让子shell把FIFO作为fd 3打开;<code>read -u 3从该fd读取一行,成功即表示拿到许可;子shell退出后fd 3自动关闭,FIFO恢复可被下一个任务打开的状态。整个过程无竞态、无资源泄漏、不依赖sem或flock等额外命令。
为什么比简单&更可靠
直接用sh a.sh & sh b.sh &的问题在于:所有进程瞬间启动,CPU/内存/IO全量冲击。而FIFO限速器提供的是内核级流控:
- 并发数严格等于
MAX_CONCURRENCY,不会多一个 - 任务失败不影响后续调度,失败进程退出即释放槽位
- 无需维护进程PID列表或轮询状态,靠文件描述符生命周期自动管理
- 可跨脚本复用同一FIFO,适合构建统一任务队列中间件
这种设计已在日均百万级日志压缩、批量证书签发等场景稳定运行多年。











