记录学习笔记、分享资源工具、交流技术思想、提升工作效率

shell实现并发控制

运维 xiaomudk 6年前 (2015-07-18) 3333次浏览 0个评论
文章目录[隐藏]

bash shell的并发,实际上是开多个子shell去执行任务,可以理解成多进程形式

1.最简单的方式实现shell并发

#!/bin/bash

function func() {
    now_time=`date`
    echo ${1}"-->"$now_time
    sleep 2
}

for i in `seq  0 9`;do
    func $i &     #分发任务
done
wait

输出结果:

0-->Wed Jul 15 17:54:31 CST 2015
2-->Wed Jul 15 17:54:31 CST 2015
7-->Wed Jul 15 17:54:31 CST 2015
5-->Wed Jul 15 17:54:31 CST 2015
4-->Wed Jul 15 17:54:31 CST 2015
3-->Wed Jul 15 17:54:31 CST 2015
9-->Wed Jul 15 17:54:31 CST 2015
8-->Wed Jul 15 17:54:31 CST 2015
1-->Wed Jul 15 17:54:31 CST 2015
6-->Wed Jul 15 17:54:31 CST 2015

使用&来启动一个子shell放在后台去执行对应的任务,最后使用wait来等待子shell结束

2.控制并发数

我们很多都需要控制并发数,不然对机器压力很大, 下面这个实例就是通过文件标识符和实名管道一块来实现进程数的控制:

#!/bin/bash
parallel_num=2     #设置并发数
tmpfile=$$.fifo    #临时文件,叫什么名字都行
mkfifo $tmpfile    #建立实名管道
exec 5<>$tmpfile   #创建文件标识符5,<>表示可对实名管道$tmpfile进行读或写
rm -f $tmpfile     #删除临时文件(可以不删的,但是为了避免程序执行期间,其它程序或用户对这个文件进行操作)

#我们想要并发运行的程序
function func() {     
    now_time=`date`
    echo ${1}"-->"$now_time
    sleep 2
}

#往&5(&5表示文件标识符5对应的地址,这里理解成一段内存空间吧)里写入信息,有多少并发数就写多少行
for i in `seq $parallel_num`;do 
    echo       # 每个echo是一行, echo 什么内容都行,我这里是echo空行
done >&5           # 注意:>和&5之间没有空格

#执行任务
for i in `seq 10`
do
  read      #从&5里读入一行信息
  ( func $i; echo >&5 ) &       #注意这里,分号后面还会有一个echo >&5,再往&5里写一行
done <&5

wait
exec 5>&-   #关闭&5

输出结果:

1-->Thu Jul 16 14:52:06 CST 2015
2-->Thu Jul 16 14:52:06 CST 2015
3-->Thu Jul 16 14:52:08 CST 2015
4-->Thu Jul 16 14:52:08 CST 2015
5-->Thu Jul 16 14:52:10 CST 2015
6-->Thu Jul 16 14:52:10 CST 2015
7-->Thu Jul 16 14:52:12 CST 2015
8-->Thu Jul 16 14:52:12 CST 2015
9-->Thu Jul 16 14:52:14 CST 2015
10-->Thu Jul 16 14:52:14 CST 2015

看输出结果,1和2是同一时间,3和4晚了2秒,这样就实现了并发数的限制。

3.控制进程数原理:

a.实名管道

使用mkfifo来建立一个实名管道(FIFO),FIFO即First Input First Output。

FIFO读写规则:

阻塞(缺省设置):

只读open

  • FIFO已经被只写打开:成功返回
  • FIFO没有被只写打开:阻塞到FIFO被打开来写

只写open

  • FIFO已经被只读打开:成功返回
  • FIFO没有被只读打开:阻塞到FIFO被打开来读

从空FIFO中read

  • FIFO已经被只写打开:阻塞到PIPE或FIFO中有数据或者不再为写打开着
  • FIFO没有被只写打开:返回0(文件结束符)

write

  • FIFO已经被只读打开:

FIFO如果只是读或写的话,就会堵塞等待,这样也不能实现我们想要的控制并发数,因为在执行第一个echo 的时候,FIFO就已经堵塞了,需要等待一个read来释放这个堵塞,这时候我们就需要用文件标识符了(我这里理解成把后续的echo缓存起来了,等待read)。

b.文件描述符

上面通过 exec 5<>filename来新建一个文件描述符5, 我们echo了两次到&5, 而read每次去读一行。

等read两次之后,&5里为空。

那么第三个read就等于堵塞(因为这个文件描述符指向的文件是FIFO文件),等待&5里写入。

而每次任务执行完之后就会执行一个echo写入,第三个read读取到内容,释放堵塞,去执行接下来的任务。进而实现控制并发数。

4.完整的并发实例

#!/usr/bin/ksh
# SCRIPT: ptest.sh
# AUTHOR: Ray001
# DATE: 2008/10/03
# REV: 2.0
# For STUDY
#
# PURPOSE:
# 实现进程并发,提高执行效率,同时能记录每个执行失败的子进程信息

#定义并发进程数量
PARALLEL=3 
#定义临时管道文件名
TMPFILE=$$.fifo
#定义导出配置文件全路径名
CMD_CFG=$HOME/cfg/ptest.cfg
#定义失败标识文件
FAILURE_FLAG=failure.log

####################### 函数定义 ########################
# 中断时kill子进程
function trap_exit
{
kill -9 0
}

# 通用执行函数
exec_cmd()
{
    # 此处为实际需要执行的命令,本例中用sleep做示例
        sleep ${1}
    if [ $? -ne 0 ]
    then
        echo "命令执行失败"
        return 1
    fi
}

trap 'trap_exit; exit 2' 1 2 3 15

#清理失败标识文件
rm -f  ${FAILURE_FLAG}

#为并发进程创建相应个数的占位
mkfifo $TMPFILE 
exec 4<>$TMPFILE 
rm -f $TMPFILE 
{ 
        count=$PARALLEL
        while [ $count -gt 0 ]
        do 
                echo
                let count=$count-1
        done 
} >&4

#从任务列表 seq 中按次序获取每一个任务 
while read SEC 
do 
        read <&4
         (  exec_cmd ${SEC} || echo ${SEC}>>${FAILURE_FLAG} ; echo >&4 ) &
done<$CMD_CFG
wait 
exec 4>&- 

#并发进程结束后判断是否全部成功
if [ -f ${FAILURE_FLAG} ]
then
        exit 1
else
        exit 0
fi

转自: http://bbs.chinaunix.net/thread-1299848-1-1.html

参考资料:

Linux I/O重定向的一些小技巧
FIFO wiki


本网站采用知识共享署名-相同方式共享 4.0 国际许可协议进行授权
转载请注明原文链接:shell实现并发控制
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址