shell - 限制任务并发
- 遇到问题
- 如何解决
- 知识点
遇到问题
数千个任务同时执行会导致服务器资源不足, 处理速度越来越慢直到夯住、崩溃
如何解决
通过限制任务最大并发数来解决, 下面提供两种方式, 假设循环 sh aaa.sh
- 通过
ps -ef | grep aaa.sh
获取当前任务并发数, 来判断并决定下一步操作
优点是简单方便快速; 缺点是限制并发数不够精确 (会出现同一时间超过10个任务执行的情况)
while true
docount=`ps -ef | grep aaa.sh | wc -l`if [ $count -lt 10 ];thensh aaa.shelse# 并行度 >= 10, 等待后进入下一次循环判断sleep 30fi
done
- 利用命名管道读操作的阻塞性来限制最大并发数
优点是精确控制并发数; 缺点是稍微麻烦难理解一点 (管道、文件描述符)
# 创建命名管道、关联文件描述符、删除命名管道
mkfifo /tmp/f6
exec 6<>/tmp/f6
rm -rf /tmp/f6# 最大并发 10
concurrency=10
for ((i=1;i<=${concurrency};i++));doecho >&6
donewhile true
do# 读取: 如缓冲区不为空, 锁定一个并发量, 后台执行一个任务;# 如缓冲区为空则读取发生阻塞, 直到已启动的任务执行完成并释放一个并发量read -u6{sh aaa.sh# 写入: 任务执行完成, 释放一个并发量echo >&6} &
donewait
# 关闭文件描述符的读和写
exec 6<&-
exec 6>&-
知识点
在管道中, 数据通过内核缓冲区进行传输:
当一个进程向管道中写入数据时, 如果缓冲区已满, 则写入操作会被阻塞, 直到另一个进程从管道中读取数据为止
当一个进程从管道中读取数据时, 如果缓冲区为空, 则读取操作会被阻塞, 直到另一个进程向管道中写入数据为止
mkfifo /tmp/f6
: 创建一个名为 /tmp/f6 的 FIFO 文件, 也称为命名管道- 多个进程可以同时打开同一个命名管道进行读写操作
- 读一个少一个, 从管道中读取数据后, 缓冲区的数据将被删除
exec 6<>/tmp/f6
文件描述符与命名管道关联- 文件描述符 6 与命名管道关联后, 可以通过 6 来对管道进行读写
rm -rf /tmp/f6
: 删除命名管道- 注意: 删除该 FIFO 文件并不会影响已经打开该文件的进程, 因此仍可以使用文件描述符 6 进行读写操作
{ }&
将一个或一组命令放入后台运行, 以便继续执行其他命令wait
等待上面命令全部执行完成后, 再执行下面的命令