实践杂谈(1)—— Bash脚本实现并行化和进程数控制

在项目过程中,我们常常需要对大量文件进行批量处理。然而,如果每个文件都需要一定的处理时间,而文件数量又很大,逐个的处理会耗费大量的时间,大大影响工作的效率。这时,如果各个文件的处理是相互独立的话,我们自然希望多个文件能够并行化地进行处理,最大限度地利用计算机资源来提高工作效率。

本文介绍两种最为常见的利用Bash脚本实现并行化的方法。值得一提的是,由于硬件条件的限制,我们通常会对并行化的进程数目进行控制。(博主便曾因为占用太多公共资源而遭到投诉了。。)

——————————————————————————————————————————————————————

  1. 后台 —— 最朴素的并行化
  2. FIFO —— 常见的进程控制方法
  3. xargs —— 逆天的方法

1. 后台操作

在linux中,由于后台运行的进程不会影响前台的操作,我们可以通过将正在运行的进程放到后台运行来实现进程的并行操作。将进程放到后台的方法有两种:
1. &
通过在执行命令时在命令行最后加上“&”符号,便可简单实现进程的后台操作。
2. Ctrl Z + bg
Ctrl Z 实现的是讲运行的进程挂起,bg实现将进程放到后台继续运行。

两种方法都会返回一个该进程的后台编号,表示该进程正在后台运行。我们可以通过 jobs 命令查看当前后台进程,也可以通过 fg % 将进程放回前台。



2. FIFO 命名管道

为了方便讨论,我们拿以下简单程序段作为文件处理的核心代码:



  • 什么是FIFO?

FIFO(命名管道)是一种进程之间进行 通信 的机制,它是一种特殊的文件类型,可以像平常的文件一样进行类似的读写操作,但又像匿名管道一样具有进程通信的功能。命名管道相对于匿名管道的优势在于,不要求进程之间具有亲缘关系,能够实现无关进程之间的通信。

  • 如何用FIFO实现并行化?

为了实现多个进程并行化,我们创建一个FIFO文件作为进程池,并通过设置一定数目的“令牌”实现并发进程的数目。每个进程依次进入进程池领取“令牌”,若领取成功(有空余令牌)则运行,完成后归还令牌;若领取失败,则等待其它进程释放令牌。

闲话少说,我们直接看代码吧:

Nproc=3	# the limit number of processes
Pfifo="/tmp/$$.fifo"    # create a fifo type file
mkfifo $Pfifo     # create a named pipe
exec 6<>$Pfifo     # fd is 6
rm -f $Pfifo

# Initialize the pipe
for((i=1; i<=$Nproc; i=i+1)); do
    echo
done >&6

filelist="text.txt"
# Loop
while read line
do
    read -u6
    {
        ./test.sh $fn
        echo >&6
    } &
done < $filelist

wait     # waiting for all the background processes finished
exec 6>&-
 
  

我们来逐行分析一下:

1. Nproc=3 设定同时执行的进程数上限

2. 新建一个后缀为fifo的FIFO文件,以PID作为文件名以避免出现重名情况

3. mkfifo命令,以上面的文件名创建一个命名管道

4. 以读写方式打开命名管道,并设置文件标识符为6

5. 删除FIFO文件,可有可无

8~10. 往命名管道中写入Nproc个空行,用来模拟Nproc个令牌

16. 从命名管道中读取一行,模拟领取一个令牌。由于FIFO特殊的读写机制,若没有空余的行可以读取,则进程会等待直至有可以读取的空余行

17~20. 若领取到令牌,运行核心程序段,完成后往命名管道写入一行,模拟归还令牌操作;这些操作都是在后台完成,故之后加上 & 命令

23. 等待所有后台进程完成

24. 释放文件标识符

3. xargs

上面啰嗦了这么多,最后我们介绍一种极其简单的方法,一行代码解决并行化操作以及控制进程数目两个任务!

xargs可以理解为一个用于传递参数列表的命令,结合 cat 和 pipe 命令,我们可以高效地将文件中每行内容作为程序的输入参数并实现并行化。继续沿用上面的例子,我们只需要写这么一行代码:

cat text.txt | xargs -L 1 -P 3 -I {} ./test.sh {}

难以置信?我们来写个小脚本测试一下它的效果吧:

实践杂谈(1)—— Bash脚本实现并行化和进程数控制_第1张图片


实验结果:

实践杂谈(1)—— Bash脚本实现并行化和进程数控制_第2张图片


简直是又简单又强大有木有!!
下面来介绍一下里面的奥妙吧。
-L 参数控制每次读取的最大行数,当然是一行一行读咯。
-P 参数控制最大使用的进程数目,这里就是实现并行化和进程数控制的关键所在啦!(未免也太方便。。)
-I 参数控制需要替换的参数位置。

That's all! 意犹未尽?那我们再来看几个小例子,体会一下xargs命令的强大之处吧!

  • 删除文件名包含空格的文件
find . -names "*.txt" -print0 | xargs -0 rm  -rf

  • 结合 grep 命令
find . -name '*.c' | xargs grep 'stdlib.h'


Reference:

  1. Bash脚本实现批量作业并行化:http://blog.sciencenet.cn/blog-548663-750136.html

  2. Linux下进程通信:命名管道: http://cpp.ezbty.org/content/science_doc/linux下进程间通信:命名管道_mkfifo

  3. 进程间通信——使用匿名管道: http://blog.csdn.net/ljianhui/article/details/10168031

  4. 进程间通信——使用命名管道:http://blog.csdn.net/ljianhui/article/details/10202699

特别鸣谢:Yuncheng Li 学长的耐心指导!


你可能感兴趣的:(Computer,Science,bash,并行化,linux,命名管道)