shell 实现并发,并控制并发数量

为了方便理解,一步步的来
首先先看一下串行的:

#! /bin/bash

ST=$(date +%s)
for i in $(seq 1 10)
do
        echo $i
        sleep 1 # 模拟程序、命令
done

ET=$(date +%s)
TIME=$(( ${ET} - ${ST} ))
echo "time: ${TIME}"

输出结果:

1
2
3
4
5
6
7
8
9
10
time: 10

这就最原始的进程运行模拟,串行方式,无法有效利用计算机的资源,很浪费切耗时。

我们可以把进程放入后台运行,这样的可以达到并发执行的效果:

#! /bin/bash

ST=$(date +%s)
for i in $(seq 1 10)
do

  {
    echo $i
    sleep 1 # 模拟程序、命令
  }& # 把循环体放入后台运行,相当于是起一个独立的线程,在此处的作用就是相当于起来10个并发
done
wait # wait命令的意思是,等待(wait命令)上面的命令(放入后台的)都执行完毕了再往下执行,通常可以和&搭配使用

ET=$(date +%s)
TIME=$(( ${ET} - ${ST} ))
echo "time: ${TIME}"

执行的结果:

1
2
3
4
5
6
7
8
9
10
time: 1

有次可见,执行后时间由原来的10缩短到了1秒,这是很大的改进
但是有个问题就是,这里我的总任务才是10个,那如果我的任务是1000个,那么计算机资源就不足了,就容易造成宕机。
所以需要控制并发数据量,之前我想过很多方法,比如切割任务,和控制循环的时间等等,都不太优雅,查阅资料,找到一个可行方案:使用管道和文件描述符。

管道:
因为我对这方面也是新知识,所以一开始就看代码,有点懵,先看点理论知识。
首先说是管道,有无名管道和有名管道
无名管道,在日常使用频率超高,比如:

ps -ef | grep java
ls | wc -l

这里面的“|”就是管道,它将前一个命令的结果输出到后一个进程中,作为两个进程的数据通道,不过他是无名的。

使用mkfifo命令创建的管道即为有名管道,例如,mkfifo pipefile, pipefile即为有名管道
有名管道有一个显著的特点:
如果管道里没有数据,那么去取管道数据时,程序会阻塞住,直到管道内进入数据,然后读取才会终止这个操作,反之,管道在执行写入操作时,如果没有读取操作,同样会阻塞

由此可以得到的有用内容就是:利用有名管道的特性就可以实现一个队列的控制。

验证就不做了,网上验证资料很多。

文件描述符:
这个就不过多说了,网上资料也很多。
但是值得注意的是,文件描述符的值不能乱取,取值范围是3-(ulimit -n)-1
ulinit -n是 系统的open files
默认一般就是1024, 直接输入ulimit -n就可以查看

所以代码

#! /bin/bash

ST=$(date +%s)
[ -e /tmp/fd1 ] || mkfifo /tmp/fd1 # 创建有名管道
exec 5<>/tmp/fd1 # 创建文件描述符,以可读(<)可写(>)的方式关联管道文件,文件描述符5拥有有名管道文件的所有特性
rm -rf /tmp/fd1 # 文件描述符关联后拥有管道的所有特性,所有删除管道
NUM=$1 # 获取输入的并发数

for (( i=1;i<=${NUM};i++ ))
do
  echo >&5 # &5表示引用文件描述符5,往里面放置一个令牌
done

for i in $(seq 1 100)
do
  read -u5
  {
    echo $i
    sleep 1 # 模拟程序、命令
    echo >&5 # 执行完把令牌放回管道
  }& # 把循环体放入后台运行,相当于是起一个独立的线程,在此处的作用就是相当于起来10个并发
done
wait # wait命令的意思是,等待(wait命令)上面的命令(放入后台的)都执行完毕了再往下执行,通常可以和&搭配使用

ET=$(date +%s)
TIME=$(( ${ET} - ${ST} ))
echo "time: ${TIME}"

运行:

mshing@remtor:~/code$ ./Concurrent.sh 20
1
2
3
4
5
6
......
95
96
97
98
99
100
time: 5

代码分析
举个例子吧,就拿以前我们学车来说,我们学车的时候就是车少人多,假如有20人练车,但是只有5辆教练车,车钥匙放在教练那里,每个人去练车要去和教练取钥匙练完放回教练那里。那么运行就是每次只能5人同时练习,等有人把车钥匙放回教练那里,下一个才能得到钥匙去练车。只到20个人练完。任务就算完成

该并发是可用的可行的
把里面的循环范围和命令改一下就可批量并发执行了

最开始产生的需求源于自己的工作,因为工作中需要对数据库的700多张表同时truncate,然是我们的工具很不好使用,而且工具里面操作的也是串行的,有时候truncate表重建表速度更快一些,但是这样是不规范的操作,我们不应该去动表结构。所以产生了使用shell去操作数据库的想法,所以就来做个小脚本。

相关文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注