shell 多线程示例
shell脚本本身不支持多线程语法,这里所说的多线程本质上是使部分代码在后台运行(多进程),使cpu自动的调用各个进程,最大限度的利用cpu的性能。
这里所说的能使用多线程的场景值得是部分代码块可以并行执行,相互之间没有影响的情况。
使用&符号后台运行多个代码块
#!/bin/bash
echo "begin"
for num in `seq 1 5`
do
{
sleep 1
echo ${num}
} &
done
echo "end"
则输出结果为:
begin
end
1
2
3
4
5
这是由于具体处理逻辑的代码放到后台执行了,没有阻塞子进程,所以end会先打印出来。为解决这种情况需要使用wait,等待当前脚本下的所有子进程退出。
使用wait控制代码整体运行逻辑
#!/bin/bash
echo "begin"
for num in `seq 1 5`
do
{
sleep 1
echo ${num}
} &
done
wait
echo "end"
则结果可能为:
begin
2
4
1
3
5
end
可以看出中间代码块的运行顺序并不是与脚本中正常运行的顺序一致,这是由于这五个代码块相互独立,执行顺序由cpu的调度决定。
但这样依然存在一个问题:因为&使得所有循环体内的命令全部进入后台运行,那么倘若循环的次数很多,会使操作系统在瞬间创建出所有的子进程,这会非常消耗系统的资源。如果循环体内的命令又很消耗系统资源,则结果可想而知。
使用命名管道控制并发数量
#/bin/bash
all_num=10
# 设置并发的进程数
thread_num=5
echo begin
# mkfifo
tempfifo="my_temp_fifo"
mkfifo ${tempfifo}
# 使文件描述符为非阻塞式
exec 6<>${tempfifo}
rm -f ${tempfifo}
# 为文件描述符创建占位信息
for ((i=1;i<=${thread_num};i++))
do
{
echo ""
}
done >&6
#
for num in `seq 1 ${all_num}`
do
{
read -u6
{
sleep 1
echo ${num}
echo "" >&6
} &
}
done
wait
# 关闭fd6管道
exec 6>&-
echo end
上述脚本总循环10次,但是最多只有5个后台进程在运行。关键点在于文件描述符,首先初始化的时候像文件描述符6中写入5个占位符,在执行循环的时候最多也就能从文件描述符中读取5次,创造5个后台运行的代码块,
当有进程运行完,会再向文件描述符中写入一个占位符,那么下一个循环才能进入后台运行,从而控制了并发的数量。