【shell二三轶事】多线程如何实现?原子操作如何实现?

【前言】

shell中并没有真正的多线程,我们这里讲的多线程,实际上是多进程,即最大化地使用cpu。

【正文】

1. 初级版

在linux或shell中,&一般放在某条命令的末尾,可以把该条命令放到一个后台进程去处理。
比如:

ls &

这个时候,我们应该可以想到一种多线程的实现方式。
那就是,循环。

for item in xxx
do
    耗时操作 &
done
wait

这里,wait的作用就是等到所有的后台进程执行完毕,才会执行后面的命令。
这时候,出现一个新的问题。
我们每循环一次就会打开一个后台进程,循环次数少的时候尚可,循环次数多了怎么办?
后台进程开的太多,我们的系统难免会崩溃,那么我们如何限制后台进程开启的数量呢?

2. 进阶版

对于上面的问题,我们很容易想到一种解决方法。
那就是,两层循环。

threads=20
for item in xxx
do
    for((i=0;i<$threads;i++))
    do
        耗时操作 &
    done
    wait
done

这个方法,其实也是有点问题的。
如果,我们同时开了20个后台进程,其中有一个跑的特别慢,那么即使其他19个进程的任务都跑完了,程序还是会继续等待。这样下来,我们的速度其实并没有多快,这种多线程是有残缺的多线程,并不完美。

3. 高级版(主流)

其实当前的主流就是使用命名管道(fifo)的方式,去实现进程数量的可控。

threads=50
mkfifo testfifo  #新建一个fifo类型的文件
exec 100<>testfifo  #以读写方式打开文件,并把文件描述符fd100指向该文件 
rm -rf testfifo #该文件可以删掉(原因是存在未关闭的fd,所以实际上文件并没有真正删除)

for((i=1;i<=${threads};i++))
do
    echo >&100  #echo默认输出一个空行,这里循环执行,相当于给该文件输入了50个空行
done

for item in xxx
do
    read -u100  #从该文件中读一行,如果没有就会卡在这里。相当于开启一个线程。
    {
        耗时操作
        echo >&100  #执行完任务后,输入一行到该文件。相当于释放一个线程。
    }&
done
wait

exec 100>&- #关闭fd100

从上面的代码和注释应该可以看的很清楚,其实控制进程数量的方法就是控制向fifo文件中读写行
至于为什么使用fifo文件,而不是使用普通文件。我查了一些文章以后发现,在默认情况下,fifo文件是阻塞的。
也就是说,如果fifo文件中没有数据,read是会卡住的,所以必须要使用fifo。实际上,fifo文件是进程间通信的一种重要手段,有兴趣的同学一定要深入看一看。

4. 番外篇(原子操作)

为什么会有这个番外篇?实际上是在工作中遇到一个需求,就是在耗时操作之后,我要打印进度。但是打印进度这个事情,无论如何也不可能是原子操作,那就会出现线程安全问题。所以,我必须找到一种方法,可以实现一整块代码的原子操作。
那就是,加锁。

# 以下是临界区
(
    flock -x 7 7>&7  #flock文件锁,-x表示独享锁
    打印进度
)7<>temp.lock

【后记】

感谢大佬们的观看,如果有不懂的地方可以随时骚扰我。
如果发现错误或可以改进的地方,也希望大佬们不吝赐教,多谢。

你可能感兴趣的:(【shell二三轶事】多线程如何实现?原子操作如何实现?)