Shell之多线程

满堂花醉三千客,一剑霜寒十四洲。这篇文章主要讲述Shell之多线程相关的知识,希望能为你提供帮助。
单线程案例:
??cat 1.sh??

#!/bin/bash
date
for i in `seq 1 10`
do
echo sleep 5
sleep 5
done
date

??sh 1.sh?? 输出:
Wed Feb 23 11:22:00 CST 2022
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
Wed Feb 23 11:22:50 CST 2022

案例说明:脚本按顺序处理10个需求,每个需求处理时长为5秒钟,单线程处理预期耗时为10*5=50s。结果符合预期。
适用场景:前后任务存在依赖。该案例前后任务不存在依赖,可以使用多线程方法减少耗时。


多线程改造案例:
??cat 2.sh??
#!/bin/bash
date
for i in `seq 1 10`
do

echo sleep 5
sleep 5
&
done
wait
date

??sh 2.sh??
Wed Feb 23 11:29:30 CST 2022
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
sleep 5
Wed Feb 23 11:29:35 CST 2022

案例说明:改造案例一的脚本,原理是借用?for循环?,将任务块?...?添加?& ?后缀的方式置为后台运行,再使用?wait?命令等后台任务执行完成后结束脚本状态。
适用于预期任务并发少的场景。
优点:所有任务可以同时处理,耗时短;                                                  缺点:如果任务并发较多,超出系统负载会导致任务失败和服务器性能不稳定等问题。
【Shell之多线程】如何解决并发较多、不可控?                                                                  答:预设并发数量,把任务以队列的方式在预设并发内进行处理。
多线程可控并发案例:
??cat 3.sh??
#!/bin/bash
date

function my_task()
echo "sleep 4"
sleep 4


fifofile="/tmp/$$.fifo"
mkfifo $fifofile
exec 6< > $fifofile
rm -rf $fifofile

thread_num=5
job_num=20

for ((i=0; i< $thread_num; i++)); do
echo > & 6
done

for ((i=0; i< $job_num; i++)); do

read -u6
my_task
echo > & 6
&
done

wait
exec 6< & -
exec 6> & -

date

??sh 3.sh??
Wed Feb 23 16:28:30 CST 2022
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
sleep 4
Wed Feb 23 16:28:46 CST 2022

[admin@vm010010012061 /home/admin]
$ps -ef | grep "sleep 4" | grep -v grep
admin60736607320 16:28 pts/200:00:00 sleep 4
admin60738607340 16:28 pts/200:00:00 sleep 4
admin60741607330 16:28 pts/200:00:00 sleep 4
admin60747607400 16:28 pts/200:00:00 sleep 4
admin60749607430 16:28 pts/200:00:00 sleep 4

[admin@vm010010012061 /home/admin]
$ps -ef | grep "sleep 4" | grep -v grep
admin61072607370 16:28 pts/200:00:00 sleep 4
admin61073607500 16:28 pts/200:00:00 sleep 4
admin61074607390 16:28 pts/200:00:00 sleep 4
admin61075607510 16:28 pts/200:00:00 sleep 4
admin61076607540 16:28 pts/200:00:00 sleep 4

[admin@vm010010012061 /home/admin]
$ps -ef | grep "sleep 4" | grep -v grep
admin61368607520 16:28 pts/200:00:00 sleep 4
admin61369607440 16:28 pts/200:00:00 sleep 4
admin61370607460 16:28 pts/200:00:00 sleep 4
admin61371607560 16:28 pts/200:00:00 sleep 4
admin61372607420 16:28 pts/200:00:00 sleep 4

[admin@vm010010012061 /home/admin]
$ps -ef | grep "sleep 4" | grep -v grep
admin61995607530 16:28 pts/200:00:00 sleep 4
admin61996607450 16:28 pts/200:00:00 sleep 4
admin61997607550 16:28 pts/200:00:00 sleep 4
admin61998607350 16:28 pts/200:00:00 sleep 4
admin62000607480 16:28 pts/200:00:00 sleep 4



案例说明:解决并发不可控问题,预设并发为5个;任务块为my_task函数,测试任务数为20个;每个并发处理4个任务,每个任务执行时长为4秒;预计完成所有任务耗时(20÷5)* 4 = 16秒。符合预期。
脚本逻辑:
第一块代码:定义任务,放入函数中,方便下面调用。
第二块代码:定义并发使用的通道。创建一个管道文件$$.fifo,并将管道文件定义到6号文件描述符(扩展:了解exec和文件描述符,这里的文件描述符可以自行定义,< 是读取、> 是写入,< > 是读写),意思是用6号文件描述符代替管道文件,所有这里将失效的管道文件删除。
第三块代码:预设可用并发数量,预期任务数量。
第四块代码:在6号文件描述符中创建并发位置,注意> 和& 中间没有空格。
第五块代码:创建任务队列,使用read读取并发位置,执行任务后再执行echo补充上面读取的并发位。
第六块代码:wait等待所有后台任务执行完成再执行下一步,下面两步分别是关闭读取和写入6号文件描述,这里关闭读写需要分两步操作。

    推荐阅读