shell多线程

https://www.cnblogs.com/chenjiahe/p/6268853.html



需求:并发检测1000台web服务器状态(或者并发为1000台web服务器分发文件等)如何用shell实现?


方案一:(这应该是大多数人都第一时间想到的方法吧)

思路:一个for循环1000次,顺序执行1000次任务。

实现:      

#!/bin/bash

start_time=`date+%s` #定义脚本运行的开始时间

for((i=1;i<=1000;i++))dosleep1#sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)

        echo'success'$i;      done

stop_time=`date+%s`  #定义脚本运行的结束时间

echo"TIME:`expr $stop_time - $start_time`"

运行结果:

[root@iZ94yyzmpgvZ ~]# . test.sh

success1

success2

success3

success4

success5

success6

success7

........此处省略

success999

success1000

TIME:1000

代码解析以及问题:

一个for循环1000次相当于需要处理1000个任务,循环体用sleep 1代表运行一条命令需要的时间,用success$i来标示每条任务.

这样写的问题是,1000条命令都是顺序执行的,完全是阻塞时的运行,假如每条命令

的运行时间是1秒的话,那么1000条命令的运行时间是1000秒,效率相当低,而我的要求是并发检测1000台web的存活,如果采用这种顺序的方式,

那么假如我有1000台web,这时候第900台机器挂掉了,检测到这台机器状态所需要的时间就是900s!

所以,问题的关键集中在一点:如何并发

方案二:

思路:一个for循环1000次,循环体里面的每个任务都放入后台运行(在命令后面加&符号代表后台运行)。

实现:

#!/bin/bashstart=`date +%s`#定义脚本运行的开始时间for((i=1;i<=1000;i++))

do

{

        sleep 1#sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)echo'success'$i;

}&#用{}把循环体括起来,后加一个&符号,代表每次循环都把命令放入后台运行#一旦放入后台,就意味着{}里面的命令交给操作系统的一个线程处理了#循环了1000次,就有1000个&把任务放入后台,操作系统会并发1000个线程来处理#这些任务        done   

wait            #wait命令的意思是,等待(wait命令)上面的命令(放入后台的)都执行完毕了再#往下执行。#在这里写wait是因为,一条命令一旦被放入后台后,这条任务就交给了操作系统#shell脚本会继续往下运行(也就是说:shell脚本里面一旦碰到&符号就只管把它#前面的命令放入后台就算完成任务了,具体执行交给操作系统去做,脚本会继续#往下执行),所以要在这个位置加上wait命令,等待操作系统执行完所有后台命令end=`date +%s`#定义脚本运行的结束时间

echo "TIME:`expr $end - $start`"

运行结果:

[root@iZ94yyzmpgvZ /]# . test1.sh ......

[989]  Done                    { sleep 1; echo'success'$i; }

[990]  Done                    { sleep 1; echo'success'$i; }

success992

[991]  Done                    { sleep 1; echo'success'$i; }

[992]  Done                    { sleep 1; echo'success'$i; }

success993

[993]  Done                    { sleep 1; echo'success'$i; }

success994

success995

[994]  Done                    { sleep 1; echo'success'$i; }

success996

[995]  Done                    { sleep 1; echo'success'$i; }

[996]  Done                    { sleep 1; echo'success'$i; }

success997

success998

[997]  Done                    { sleep 1; echo'success'$i; }

success999

[998]  Done                    { sleep 1; echo'success'$i; }

[999]-  Done                    { sleep 1; echo'success'$i; }

success1000

[1000]+  Done                    { sleep 1; echo'success'$i; }

TIME:2

代码解析以及问题:

shell中实现并发,就是把循环体的命令用&符号放入后台运行,1000个任务就会并发1000个线程,运行时间2s,比起方案一的1000s,已经非常快了。

可以看到输出结果success4 ...success3完全都是无序的,因为大家都是后台运行的,这时候就是cpu随机运行了,所以并没有什么顺序

这样写确实可以实现并发,然后,大家可以想象一下,1000个任务就要并发1000

个线程,这样对操作系统造成的压力非常大,它会随着并发任务数的增多,操作系统处理速度会变慢甚至出现其他不稳定因素,就好比你在对nginx调优后,你

认为你的nginx理论上最大可以支持1w并发了,实际上呢,你的系统会随着高并发压力会不断攀升,处理速度会越来越慢(你以为你扛着500斤的东西你还

能跑的跟原来一样快吗)

方案三:

思路:基于方案二,使用linux管道文件特性制作队列,控制线程数目

知识储备:

一.管道文件

1:无名管道(ps aux | grep nginx)

2:有名管道(mkfifo /tmp/fd1)

有名管道特性:

1.cat /tmp/fd1(如果管道内容为空,则阻塞)

实验:

2.echo "test" > /tmp/fd1(如果没有读管道的操作,则阻塞)

总结:

  利用有名管道的上述特性就可以实现一个队列控制了

  你可以这样想:一个女士公共厕所总共就10个蹲位,这个蹲位就是队列长度,女厕  所门口放着10把药匙,要想上厕所必须拿一把药匙,上完厕所后归 还药匙,下一个人就可以拿药匙进去上厕所了,这样同时来了1千

  位美女上厕所,那前十个人抢到药匙进去上厕所了,后面的990人 需要等一个人出来归还药匙才可以拿到药匙进去上厕所,这样10把药匙就实现了控制1000人上厕所的任务(os中称之为信号量)

二.文件描述符

1.管道具有存一个读一个,读完一个就少一个,没有则阻塞,放回的可以重复取,这正是队列特性,但是问题是当往管道文件里面放入一段内容,没人取则会阻塞,这样你永远也没办法往管道里面同时放入10段内容(想当与10把药匙),解决这个问题的关键就是文件描述符了。

2. mkfifo /tmp/fd1     

创建有名管道文件exec

3<>/tmp/fd1,创建文件描述符3关联管道文件,这时候3这个文件描述符就拥有了管道的所有特性,还具有一个管道不具有的特性:无限

存不阻塞,无限取不阻塞,而不用关心管道内是否为空,也不用关心是否有内容写入引用文件描述符: &3可以执行n次echo

>&3 往管道里放入n把钥匙

exec命令用法:http://blog.sina.com.cn/s/blog_7099ca0b0100nby8.html

实现:      


#!/bin/bash

start_time=`date+%s`              #定义脚本运行的开始时间

[ -e /tmp/fd1 ] ||mkfifo/tmp/fd1 #创建有名管道

exec 3<>/tmp/fd1                  #创建文件描述符,以可读(<)可写(>)的方式关联管道文件,这时候文件描述符3就有了有名管道文件的所有特性rm-rf /tmp/fd1                    #关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除,我们留下文件描述符来用就可以了for((i=1;i<=10;i++))doecho>&3#&3代表引用文件描述符3,这条命令代表往管道里面放入了一个"令牌"donefor((i=1;i<=1000;i++))doread -u3                          #代表从管道中读取一个令牌

{

        sleep1#sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)

        echo'success'$i     

        echo>&3                  #代表我这一次命令执行到最后,把令牌放回管道

}&donewait

stop_time=`date+%s`  #定义脚本运行的结束时间

echo"TIME:`expr $stop_time - $start_time`"exec 3<&-                      #关闭文件描述符的读

exec 3>&-                      #关闭文件描述符的写

运行结果:

[root@iZ94yyzmpgvZ /]# . test2.shsuccess4

success6

success7

success8

success9

success5

......

success935

success941

success942

......

success992

[992]  Done                    {sleep1;echo'success'$i;echo1>&3; }

success993

[993]  Done                    {sleep1;echo'success'$i;echo1>&3; }

success994

[994]  Done                    {sleep1;echo'success'$i;echo1>&3; }

success998

success999

success1000

success997

success995

success996

[995]  Done                    {sleep1;echo'success'$i;echo1>&3; }

TIME:101

代码解析以及问题:

两个for循环,第一个for循环10次,相当于在女士公共厕所门口放了10把钥匙,第二个for

循环1000次,相当于1000个人来上厕所,read -u3相当于取走一把药匙,{}里面最后一行代码echo >&3相当于上完厕所送还药匙。

这样就实现了10把药匙控制1000个任务的运行,运行时间为101s,肯定不如方案二快,但是比方案一已经快很多了,这就是队列控制同一时间只有最多10个线程的并发,既提高了效率,又实现了并发控制。

注意:创建一个文件描述符exec 3<>/tmp/fd1

不能有空格,代表文件描述符3有可读(<)可写(>)权限,注意,打开的时候可以写在一起,关闭的时候必须分开关,exec

3<&-关闭读,exec 3>&-关闭写

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 分别用python和shell写一个多线程脚本执行1~6.sh脚本(多线程数设置为 2),6个脚本执行耗时不一样,...
    LEUNGJH阅读 878评论 0 0
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,726评论 0 10
  • 个人学习批处理的初衷来源于实际工作;在某个迭代版本有个BS(安卓手游模拟器)大需求,从而在测试过程中就重复涉及到...
    Luckykailiu阅读 4,702评论 0 11
  • 第 2 章 SHELL 基础知识2.1 shell脚本我们在上面简单介绍了一下什么是shell脚本,现在我们来进一...
    LiWei_9e4b阅读 1,561评论 0 0
  • shell虽然执行效率差,但优点,是所用即所得,不用移植,基本上所有linux都通用,基本不用额外安装程序。s 当...
    banemon阅读 994评论 0 0