shell 中用管道模拟多线程 shell 中用管道模拟多线程

这里以两个例子来对比多线程和单进程

单线程的例子

# config.txt在这个例子和多线程的例子中都会用到
[root@ns_10.2.1.242 test]$ cat config.txt 
1
2
3
4
1
2
3
4

# 下面的代码,是从config.txt中读取配置,然后sleep一定时间,然后打印sleep 的时间长度,
# 注意 while从文本读取数据,是逐行读取的
[root@ns_10.2.1.242 test]$ cat while.sh 
while read line
do
    sleep $line && echo "$line success"
done < config.txt

# 这个脚本,消耗了20秒,结果如下:
[root@ns_10.2.1.242 test]$ time sh while.sh
1 success
2 success
3 success
4 success
1 success
2 success
3 success
4 success

real    0m20.011s
user    0m0.000s
sys     0m0.004s

多线程的例子:

# config.txt在这个例子和多线程的例子中都会用到
[root@ns_10.2.1.242 test]$ cat config.txt 
1
2
3
4
1
2
3
4

# 多线程的代码,如下
[root@ns_10.2.1.242 test]$ cat p3.sh 
#!bash
# 2014-12-5
# --------------------
#  此例子说明了一种用wait、read命令模拟多线程的一种技巧
#  此技巧往往用于多主机检查,比如ssh登录、ping等等这种单进程比较慢而不耗费cpu的情况
# -------------------------
operation(){
    sleep $1
}

tmp_fifofile=/tmp/$$.fifo
#echo $tmp_fifofile

mkfifo $tmp_fifofile # 新建一个fifo的管道文件
exec 6<>$tmp_fifofile # 绑定fd6
rm $tmp_fifofile

# 这里是向管道添加了$thread个空行
THREAD=3 # 线程数,可以改变
for i in $(seq 0 $THREAD);do
    echo
done >&6

CONFIG_FILE=config.txt
# 修改这个脚本到生成环境,主要是修改operation和CONFIG_FILE配置
# 每次读取一行数据
while read line
do
    # 一个read -u6命令执行一次,就从fd6中减去一个回车符,然后向下执行,
    # fd6 中没有回车符的时候,就停在这了,从而实现了线程数量控制
    read -u6

    {
       # 操作成功,记录到成功日志,修改echo
       # 操作失败,记录到错误日志
       operation  $line && echo " $line success" || echo "$line error"

       # 当进程结束以后,再向fd6中加上一个回车符,即补上了read -u6减去的那个
       echo  >&6
    } & # 后台执行,这里的 &是非常重要的,同时有$THREAD个后台进程 

done < ${CONFIG_FILE}


wait # 等待所有的后台子进程结束
exec 6>&- # 关闭df6
exit 0

# 脚本的结果,执行了7秒
[root@ns_10.2.1.242 test]$ time sh p3.sh 
 1 success
 2 success
 1 success
 3 success
 4 success
 2 success
 3 success
 4 success

real    0m7.007s
user    0m0.000s
sys     0m0.003s
[root@ns_10.2.1.242 test]$

另外一个 命名管道基础的文章

多进程脚本中需要注意的知识点,有3个

  1. while读取文件

     	while read line 
     	do
     		echo $line 
     	done < config.txt
    
  2. 管道的绑定, exec 6<>$tmp_fifofile 和 向管道写数据 echo >&6

     [root@ns_10.2.1.242 test]$ mkfifo testfifo
     [root@ns_10.2.1.242 test]$ exec  6<>testfifo 
     [root@ns_10.2.1.242 test]$ echo 1 >&6   
     [root@ns_10.2.1.242 test]$ cat testfifo 
     1
     #注意,进程会卡死的
    
     # 没有& 符号,是创建了文件名为6的文件,然后把1 输出问文件名为6的文件
     [root@ns_10.2.1.242 test]$ echo 1 >6
     [root@ns_10.2.1.242 test]$ cat 6    
     1
     [root@ns_10.2.1.242 test]$ ls -alh 6
     -rw-r--r--. 1 root root 2 Jan 22 20:26 6
    
  3. read -u6

     read 
     	-u Read input from file descriptor fd(从文件描述符读取输入)
    
     read 读取一行,向管道写n行,就可以读取n次,n+1会堵塞
     [root@ns_10.2.1.242 test]$ mkfifo testfifo
     [root@ns_10.2.1.242 test]$ exec  6<>testfifo 
     [root@ns_10.2.1.242 test]$ echo 1 >&6   
     [root@ns_10.2.1.242 test]$ echo 1 >&6
     [root@ns_10.2.1.242 test]$ echo 1 >&6
     [root@ns_10.2.1.242 test]$ read -u6
     [root@ns_10.2.1.242 test]$ read -u6
     [root@ns_10.2.1.242 test]$ read -u6
    

注意:
多线程脚本应用在真实环境的时候,只需要修改CONFIG_FILEoperation函数
THREAD控制线程数量