学习Shell脚本的多进程并发与控制

一、前言

在日常数据处理过程中,很多时候用的都是单进程处理;如果任务较大的时候,这样耗时比较久同时也浪费资源,可将任务多进程并发处理,并合理控制其进程数,将很有效的利用机器资源、减少时间。经过资料查询与实践,这里脚本的功能就是产生多进程并控制其并行进程数量。具体多进程的好处可以参考其他资料,这里就不细说了。

并行进程数超过任务总数是没有意义的,因为实际并行总数会小于等于任务总数;

二、应用场景

比如抓取网页数据,比如需要抓取100w个网页的数据,将其数据过滤后存入数据库,如果采用单进程或者手工操作肯定是很麻烦耗时的,因此,采用这种方式会很好的提高效率。但是,如果循环处理的子任务耗时过短,如:只有一个命令等,那就没有必要控制多进程,因为那样就增加了运行时间,但是一般单个任务处理时间超过1s且任务量比较大时,我个人认为多进程控制还是很有必要。

其他:批量数据文件导入数据库、数据库数据批量导出为多个文件也可以使用这种方式;

三、使用说明

  1. 如果要利用模板脚本处理任务,那么只需要在下图红色处按要求添加自己的任务处理即可(后续测试实例简单地处理了一下模板),具体源码与注释见附件(运行环境Linux bash);

<<多进程并发与控制脚本-处理流程图>>

四、测试实例

1.  我自己根据模板脚本写了一个测试例子;将指定规则的文本信息输出到1000个文件中,共有10000条记录,输出后睡眠3s(用于测试),控制进程为1000个。主要是验证是否能够达到多进程及其控制的效果。在里面处理日志的时候还加入了一些文件描述符重定向有关的东西。

2.  源码见附件(运行环境Linux bash)

注:说得比较详细;实际操作过程中可简化代码,实现功能!

由于无法附件,源码:

模板_MultiProcess.sh

##################
#简单说明:
#  多进程控制利用读取管道文件fifo实现并发进程数量的控制;
#  运行:$1 并行进程数
##################

#!/bin/bash

####目录/文件变量设置###################
TmpFifoFile=~/tmp/$$.fifo    #临时管道文件
################################

#防止异常爆发
set -eo pipefail
###########自定义函数区域BEGIN##########
#########
#此处定义需要处理的函数逻辑[   说明   ]
#
#while/for...任务逻辑
#一个read -u6命令执行一次,就从fd6中减去一个回车符,然后向下执行,
#fd6中没有回车符的时候,就停在这了,从而实现了进程数量控制
#read-u6   #读取令牌
#do
#(
#function循环子任务处理函数(此处一般不再开立子进程处理)
#当任务处理完成后,再向fd6中加上一个回车符,即补上了read -u6减去的那个,子进程>会继承父
#进程的文件描述符
#echo >&6释放令牌
#)&
#done
#wait
##########
function deal_task()
{




	return 0
}
#######自定义函数区域END#######


#创建并行进程令牌
#目前令牌用的文件符6
function create_token()
{
	proc_num=$1 #令牌数

	#获取当前登录用户最大可用进程
	UserMaxUseProcNum=$(expr $(ulimit -u) - $(ps -ef|awk -F " " '{print $1}'|grep $(whoami)|wc -l))

  if [ $# -ne 1 ]
  then
    echo "USAG: $0 并行进程数(当前用户可用${UserMaxUseProcNum},建议不超过1/3,实际最大进程数为所有子任务数)"
    return 1
  fi

	#判别输入并行进程数是否符合要求)
	test=1
	if [ ${proc_num} -gt ${UserMaxUseProcNum} ]
	then
		echo "ERROR!输入进程总数${proc_num}大于当前用户可用进程总数${UserMaxUseProcNum}"
		return 1
	fi

	#建立临时管道用于控制并发进程数
	tmp_fifofile="/tmp/$$.fifo"
	mkfifo $tmp_fifofile
	exec 6<>$tmp_fifofile     # 将fd6指向fifo文件,设置读写
	rm $tmp_fifofile  #清除管道文件
	#根据传入的进程数量定义令牌数
	for ((i=1;i<=${proc_num};i++))
	do
		echo
	done >&6
	return 0
}


#####主进程设置################

echo "脚本运行--开始----`date`-------------"
(
echo "**创建令牌 begin**"
#传入进程数量
create_token $1 ||{
echo "创建进程令牌失败"
exit 1
}
echo "**创建令牌 end**"

echo "**任务处理 begin**"
deal_task ||{
echo "任务处理失败!"
exit 1
}
echo "**任务处理 end**"
)&
#等待任务处理完毕
wait
exec 6<&-    #释放文件描述符
echo "脚本运行--结束----`date`-------------"


实例MultiProcess_CreateFile.sh

##################
#简单说明:
#  多进程控制利用读取管道文件fifo实现并发进程数量的控制;
#  运行:$1 并行进程数
#  功能: 
#  测试生成10000条记录,共1000个文件,最大并行进程数为1000
##################

#!/bin/bash

####目录/文件变量设置###################
TmpFifoFile=~/tmp/$$.fifo    #临时管道文件
OutDataPath=./data   #输出文件路径  by yuanwm
OutLogPath=./log   #输出日志文件 by yuanwm
errlogname=$$.errlog  #错误日志文件  by yuanwm
runlogname=$$.runlog  #运行日志文件  by yuanwm
################################

#防止异常爆发
set -eo pipefail
###########自定义函数区域BEGIN##########
#########
#此处定义需要处理的函数逻辑[   说明   ]
#
#while/for...任务逻辑
#一个read -u6命令执行一次,就从fd6中减去一个回车符,然后向下执行,
#fd6中没有回车符的时候,就停在这了,从而实现了进程数量控制
#read-u6   #读取令牌
#do
#(
#function循环子任务处理函数(此处一般不再开立子进程处理)
#当任务处理完成后,再向fd6中加上一个回车符,即补上了read -u6减去的那个,子进程>会继承父
#进程的文件描述符
#echo >&6释放令牌
#)&
#done
#wait
##########
function deal_task()
{
  #define by yuanwm
  #测试生成10000条记录,共1000个文件
	for((i=0;i<10000;i++))
	do
	read -u6
	(
		echo "处理任务 ${i}"
		echo "编号$i|测试企业名称$i|测试联系人$i|测试联系人号码$i" >> ${OutDataPath}/test_data_`expr $i % 1000 `.txt
		sleep 3 
		echo >&6
	)&
	done

	wait
	return 0 
}
#######自定义函数区域END#######


#创建并行进程令牌
#目前令牌用的文件符6
function create_token()
{
	proc_num=$1 #令牌数

	#获取当前登录用户最大可用进程
	UserMaxUseProcNum=$(expr $(ulimit -u) - $(ps -ef|awk -F " " '{print $1}'|grep $(whoami)|wc -l))

  if [ $# -ne 1 ]
  then
    echo "USAG: $0 并行进程数(当前用户可用${UserMaxUseProcNum},建议不超过1/3,实际最大进程数为所有子任务数)"
    return 1
  fi

	#判别输入并行进程数是否符合要求)
	if [ ${proc_num} -gt ${UserMaxUseProcNum} ]
	then
		echo "ERROR!输入进程总数${proc_num}大于当前用户可用进程总数${UserMaxUseProcNum}"
		return 1
	fi

	#建立临时管道用于控制并发进程数
	tmp_fifofile="/tmp/$$.fifo"
	mkfifo $tmp_fifofile
	exec 6<>$tmp_fifofile     # 将fd6指向fifo文件,设置读写
	rm $tmp_fifofile  #清除管道文件
	#根据传入的进程数量定义令牌数
	for ((i=1;i<=${proc_num};i++))
	do
		echo
	done>&6
	return 0 
}

#####主进程设置################
echo "脚本运行--开始----`date`-------------"
#目录创建
mkdir -p ${OutDataPath}
mkdir -p ${OutLogPath}
#日志重定向
exec 5<&1  #标准输出保存
exec 7<&2  #标准错误保存
exec 1>>${OutLogPath}/${runlogname}
exec 2>>${OutLogPath}/${errlogname}

(
	echo "**创建令牌 begin**"
	#传入进程数量
	create_token $1 ||{
	echo "创建进程令牌失败"
	exit 1
	}
	echo "**创建令牌 end**"

	echo "**任务处理 begin**"
	deal_task ||{
	echo "任务处理失败!"
	exit 1
	}
	echo "**任务处理 end**"
)&
#等待任务处理完毕
wait
exec 1<&5    #标准输出还原

exec 6<&-    #释放文件描述符
exec 5<&-    #释放文件描述符
exec 7<&-    #释放文件描述符
echo "脚本运行--结束----`date`-------------"



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值