当前位置: 首页 > Linux

Shell多进程执行任务

时间:2023-04-06 02:41:04 Linux

showcode#!/bin/bashtrap"exec1000>&-;exec1000<&-;exit0"2#分别为 创建pipeline文件,绑定文件算子,删除Pipelinefilemkfifotestfifoexec1000<>testfiform-rftestfifo#写入文件操作符。#通过一个for循环写入10个空行,这个10就是我们要定义的后台线程数。for((n=1;n<=10;n++))doecho>&1000done#如果[[!-d返回]];thenmkdirbackfi#starttimerecordstart=`date"+%s"`#获取url总数,如果总数为0则直接退出total=`caturls|wc-l`if[[$total==0]];thenecho'thetotalnumberofurlsisempty'exit0fi#遍历URLS文件,开始执行下载for((i=1;i<=$total;i++))do{#从testfifo读取一行read-u1000{#增加尝试次数,最大为5forjin{1..5}do#判断单独的进程文件目录是否存在。如果不存在,则创建目录download_dir=audio"$i"if[[!-d$下载目录]];thenmkdir-p$download_dirfiecho"Startdownloadfilesinthedirectory${download_dir},Numberofattempts:${j}"#读取URLS中的一行,下载文件you-get-o$download_dir`sed-n"$i"p网址|tr-d'\r'`#检查是否有异常,如果没有异常则跳出循环执行下一条,如果有异常则重新尝试下载if[[$?!=0]];thenmv$download_dir/*backrm-rf$download_direlsebreakfidone#向文件operatorec写入一个空行ho>&1000}&}done#等待所有任务完成waitend=`date"+%s"`echo"time:`expr$end-$start`"exec1000>&-exec1000<&-so-所谓多进程就是把一个任务分成多个子任务在后台执行,“FIFO”是一种允许独立进程通信的特殊文件类型。一个进程打开一个FIFO文件进行写入,另一个进程读取它,然后数据就可以在shell或其他地方以与匿名管道相同的方式流式传输。默认情况下,创建的FIFO的模式是0666('a+rw')减去umask中设置的位。串行、并行串行任务为了比较并行和串行的区别,我们先写一个串行脚本:#!/bin/bashstart=`date"+%s"`foriin{1..10}doecho$我;sleep2doneend=`date"+%s"`echo"Time:`expr$end-$start`"执行结果如下:$shseries.sh12345678910Time:21从结果看,以上10次每次都执行task耗时2秒,总时间21秒,符合代码逻辑。并行任务首先将上述串行任务变为多线程并行任务。#!/bin/bashstart=`date"+%s"`foriin{1..10}do{echo$i;sleep2}&donewaitend=`date"+%s"`echo"Time:`expr$end-$start`"上面脚本执行结果如下:$shparallel.sh12345678910Time:2通常用{}来包装一个不占用处理器但耗时的任务,通过&后台运行,达到节省时间的效果。在上面的并行代码中,我们在后台执行所有10个任务,每个字符需要2秒。由于并行执行,总耗时为Max(任务耗时)=2秒。{回声$我;sleep2}&在小任务面前,这种方式用起来顺手,但是当任务负载过大时,这种方式的缺点就很明显:无法控制后台运行的进程数,而且只有100,000任务意味着运??行100,000个进程。为了控制进程,我们引入了管道和文件操作符。管道和文件操作符管道就像水管。有流入才会有流出。水管是水流的通道,管道是数据的通道。管道分为无名管道和有名管道。管道类命令:栗子无名管道是常用的``管道,但是是无名的,可以直接作为两个进程之间的数据通道`echo"helloworld,I'matest"grep"test"`namedpipemkfilo可以创建一个管道文件mkfiflotestfifopipeline有一个特点。如果管道中没有数据,则取管道数据的操作会被阻塞,直到数据进入管道,读出后终止操作。同样,写入管道的操作如果没有读操作,这个动作也会阻塞。当通过echo命令向fifotest管道写入数据时,由于管道上没有其他消费者进程在操作,管道会被阻塞,直到打开另一个窗口,通过cat操作管道。同理,如果先操作读管道,也会阻塞。通过上面的实验可以看出,只用一个pipeline文件似乎很难控制后台线程的数量,所以接下来我们简单介绍一下fileoperator。当文件操作符系统开始运行时,相应的设备会自动绑定到三个文件操作符0、1、2,分别对应stdin、stdout、stderr。在/proc/self/fd或/dev/fd中可以看到这三个对应的文件:会显示输出到这三个文件的内容。只是因为显示器被绑定为最常见的输出设备。在Linux中,可以通过exec命令自行定义和绑定文件操作符。一般文件操作符可以随便使用,从3到(n-1),其中n是ulimit-n的定义值。从上图可以看出本机的n值是8192,所以文件操作符只能使用0-8192,自己可以定义的只能是3-8192。虽然exec和source都是在父进程中直接执行的,但是exec和source还是有很大区别的。source执行shell脚本,执行完返回到之前的shell。exec的执行不会返回到之前的shell,而是直接把之前的登录shell当成一个程序,复制到上面。exec可以参考这篇文章:《linux 下的 mkfifo、exec 命令使用》代码分析第三行:接受信号2(ctrl+C)完成的操作。当我们生成一个文件描述符并进行绑定时,我们可以使用exec1000<>testfifo来实现,但是关闭的时候必须单独写。>读绑定,<标记绑定<>标识所有对文件描述符1000的操作,相当于对管道文件testfifo的操作。第6-8行: 分别是创建管道文件、绑定文件操作符、删除管道文件。可能会有疑问。为什么我们不能直接使用管道文件呢?其实,这并不是多余的。刚才已经说明了管道文件的一个重要特点,就是读和写必须同时存在。如果一个操作少了,另一个操作就阻塞了,绑定文件操作符正好解决了这个问题。问题。第12-15行:写入文件操作符。通过一个for循环写入10个空行,这个10就是我们要定义的后台线程数。为什么要写一个空行而不是10个字符?这是因为管道文件是以行为单位读取的。当我们尝试使用read读取管道中的一个字符时,结果是不成功的。上面的例子已经确认可以使用cat读取。第32-61行:遍历url总行数,循环处理url。第25-29行是读取urls文件总行数的逻辑(见开篇代码)。这里我们有$total任务($total是一个变量,是读取的url总行数,值大于0),我们需要保证只有10个进程在后台同步运行(当然,这段代码有点遗憾,只是失败了,使用多少进程取决于总行数,join总数不到10,但是我们创建了10行空字符串,但这并不影响我们的测试).read-u1000的作用是:读取管道中的一行,这里是读取一个空行。减去操作附带的一个空行后,执行一个任务(当然是放在后台执行)。需要注意的是,该任务在后台执行完成后,会向文件操作符写入一个空行。这是关键点。如果我们在某个时候不给operator写空行,结果就是:后台放了10个task后,因为operator里面没有空行可以读了,read-u1000一直停在这里。第38-56行,自己处理,这里是通过you-get下载url中的图片和语音,如果下载失败,最多尝试5次。关于你-get参考这篇文章《You-Get:支持 80 多个网站的命令行多媒体下载器》了解更多。第64-69行:等待所有进程执行完毕。exec1000>&-和exec1000<&-将关闭fd1000。本文发表于《虚怀若谷》个人博客,转载请务必署名,转载请注明出处。古善即道,微妙而玄妙,深不可知。胡伟面目全非,我强行装成这样:玉熙如冬过江,菊熙如畏邻,颜夕如客,浣夕如冰释,敦熙如单纯,开阔如谷,浑若阴。谁能化浊为静许晴?谁能安心动徐盛?保持这种方式,不想获利。夫无利,可成新。请关注我的微信公众号:下雨就像弹钢琴,谢谢?(?ω?)?