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

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

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

实现:

  1. #!/bin/bash
  2. start_time=`date +%s` #定义脚本运行的开始时间
  3.  
  4. for ((i=;i<=;i++))
  5. do
  6. sleep #sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
  7. echo 'success'$i;
  8. done
  9.  
  10. stop_time=`date +%s` #定义脚本运行的结束时间
  11.  
  12. echo "TIME:`expr $stop_time - $start_time`"

运行结果:

  1. [root@iZ94yyzmpgvZ ~]# . test.sh
  2. success1
  3. success2
  4. success3
  5. success4
  6. success5
  7. success6
  8. success7
  9. ........此处省略
  10. success999
  11. success1000
  12. TIME:

代码解析以及问题:

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

这样写的问题是,1000条命令都是顺序执行的,完全是阻塞时的运行,假如每条命令的运行时间是1秒的话,那么1000条命令的运行时间是1000秒,效率相当低,而我的要求是并发检测1000台web的存活,如果采用这种顺序的方式,那么假如我有1000台web,这时候第900台机器挂掉了,检测到这台机器状态所需要的时间就是900s!

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

方案二:

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

实现:

  1. #!/bin/bash
  2. start=`date +%s` #定义脚本运行的开始时间
  3.  
  4. for ((i=1;i<=1000;i++))
  5. do
  6. {
  7. sleep 1 #sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
  8. echo 'success'$i;
  9. }& #用{}把循环体括起来,后加一个&符号,代表每次循环都把命令放入后台运行
  10. #一旦放入后台,就意味着{}里面的命令交给操作系统的一个线程处理了
  11. #循环了1000次,就有1000个&把任务放入后台,操作系统会并发1000个线程来处理
  12. #这些任务
  13. done
  14. wait #wait命令的意思是,等待(wait命令)上面的命令(放入后台的)都执行完毕了再
  15. #往下执行。
  16. #在这里写wait是因为,一条命令一旦被放入后台后,这条任务就交给了操作系统
  17. #shell脚本会继续往下运行(也就是说:shell脚本里面一旦碰到&符号就只管把它
  18. #前面的命令放入后台就算完成任务了,具体执行交给操作系统去做,脚本会继续
  19. #往下执行),所以要在这个位置加上wait命令,等待操作系统执行完所有后台命令
  20. end=`date +%s` #定义脚本运行的结束时间
  21.  
  22. echo "TIME:`expr $end - $start`"

运行结果:

  1. [root@iZ94yyzmpgvZ /]# . test1.sh
  2. ......
  3. [989] Done { sleep 1; echo 'success'$i; }
  4. [990] Done { sleep 1; echo 'success'$i; }
  5. success992
  6. [991] Done { sleep 1; echo 'success'$i; }
  7. [992] Done { sleep 1; echo 'success'$i; }
  8. success993
  9. [993] Done { sleep 1; echo 'success'$i; }
  10. success994
  11. success995
  12. [994] Done { sleep 1; echo 'success'$i; }
  13. success996
  14. [995] Done { sleep 1; echo 'success'$i; }
  15. [996] Done { sleep 1; echo 'success'$i; }
  16. success997
  17. success998
  18. [997] Done { sleep 1; echo 'success'$i; }
  19. success999
  20. [998] Done { sleep 1; echo 'success'$i; }
  21. [999]- Done { sleep 1; echo 'success'$i; }
  22. success1000
  23. [1000]+ Done { sleep 1; echo 'success'$i; }
  24. 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

实现:

  1. #!/bin/bash
  2. start_time=`date +%s` #定义脚本运行的开始时间
  3. [ -e /tmp/fd1 ] || mkfifo /tmp/fd1 #创建有名管道
  4. exec <>/tmp/fd1 #创建文件描述符,以可读(<)可写(>)的方式关联管道文件,这时候文件描述符3就有了有名管道文件的所有特性
  5. rm -rf /tmp/fd1 #关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除,我们留下文件描述符来用就可以了
  6. for ((i=;i<=;i++))
  7. do
  8. echo >& #&3代表引用文件描述符3,这条命令代表往管道里面放入了一个"令牌"
  9. done
  10.  
  11. for ((i=;i<=;i++))
  12. do
  13. read -u3 #代表从管道中读取一个令牌
  14. {
  15. sleep #sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
  16. echo 'success'$i
  17. echo >& #代表我这一次命令执行到最后,把令牌放回管道
  18. }&
  19. done
  20. wait
  21.  
  22. stop_time=`date +%s` #定义脚本运行的结束时间
  23.  
  24. echo "TIME:`expr $stop_time - $start_time`"
  25. exec <&- #关闭文件描述符的读
  26. exec >&- #关闭文件描述符的写

运行结果:

  1. [root@iZ94yyzmpgvZ /]# . test2.sh
  2. success4
  3. success6
  4. success7
  5. success8
  6. success9
  7. success5
  8. ......
  9. success935
  10. success941
  11. success942
  12. ......
  13. success992
  14. [] Done { sleep ; echo 'success'$i; echo >&; }
  15. success993
  16. [] Done { sleep ; echo 'success'$i; echo >&; }
  17. success994
  18. [] Done { sleep ; echo 'success'$i; echo >&; }
  19. success998
  20. success999
  21. success1000
  22. success997
  23. success995
  24. success996
  25. [] Done { sleep ; echo 'success'$i; echo >&; }
  26. TIME:

代码解析以及问题:

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

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

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

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

本文转自http://egon09.blog.51cto.com/9161406/1754317

shell队列实现线程并发控制(转)的更多相关文章

  1. shell 队列实现线程并发控制

    需求:并发检测1000台web服务器状态(或者并发为1000台web服务器分发文件等)如何用shell实现? 方案一:(这应该是大多数人都第一时间想到的方法吧) 思路:一个for循环1000次,顺序执 ...

  2. 拆开Ceph看队列和线程

    作者:吴香伟 发表于 2017/01/08 版权声明:可以任意转载,转载时务必以超链接形式标明文章原始出处和作者信息以及版权声明 我上小学时家离学校很远,家在某某山脚,学校在镇里.每周回家一趟,周五放 ...

  3. 自定义ThreadPoolExecutor带Queue缓冲队列的线程池 + JMeter模拟并发下单请求

    .原文:https://blog.csdn.net/u011677147/article/details/80271174 拓展: https://github.com/jwpttcg66/GameT ...

  4. python队列、线程、进程、协程

    目录: 一.queue 二.线程 基本使用 线程锁 自定义线程池 生产者消费者模型(队列) 三.进程 基本使用 进程锁 进程数据共享 默认数据不共享 queues array Manager.dict ...

  5. 6、TensorFlow基础(四)队列和线程

    队列和线程 和 TensorFlow 中的其他组件一样,队列(queue)本身也是图中的一个节点,是一种有状态的节点,其他节点,如入队节点(enqueue)和出队节点(dequeue),可以修改它的内 ...

  6. python队列、线程、进程、协程(转)

    原文地址: http://www.cnblogs.com/wangqiaomei/p/5682669.html 一.queue 二.线程 #基本使用 #线程锁 #自定义线程池 #生产者消费者模型(队列 ...

  7. 【Java并发】并发队列与线程池

    并发队列 阻塞队列与非阻塞队 ConcurrentLinkedQueue BlockingQueue ArrayBlockingQueue LinkedBlockingQueue PriorityBl ...

  8. 多线程多进程学习threading,queue线程安全队列,线程间数据状态读取。threading.local() threading.RLock()

    http://www.cnblogs.com/alex3714/articles/5230609.html python的多线程是通过上下文切换实现的,只能利用一核CPU,不适合CPU密集操作型任务, ...

  9. 我们一起来学Shell - shell的并发及并发控制

    文章目录 bash的并发 未使用并发的脚本 简单修改 使用wait命令 控制并发进程的数量 文件描述符 查看当前进程打开的文件 自定义当前进程用描述符号操作文件 管道 我们一起来学Shell - 初识 ...

随机推荐

  1. 挖一挖不常用到而又很实用的重载-Trim

    这个我想没有那个开发人员说不知道,但是里面有一个重载,这个不知道有多少开发人员知道! 可以看到,我可以去掉字符串前后的指定字符,只要我在char[]中指定即可,而不是仅仅去掉空格,这次为什么要提它,是 ...

  2. Axure RP一个专业的快速原型设计工具

    Axure RP是一个专业的快速原型设计工具.Axure(发音:Ack-sure),代表美国Axure公司:RP则是Rapid Prototyping(快速原型)的缩写. Axure简要介绍 Axur ...

  3. flask中jinjia2模板引擎使用详解5

    接上文 宏 可以理解为函数,即把一些常用的模板片段做好封装,以便于重用,减少工作量和维护难度. 宏的定义很简单: {%macro xxx()%} ##这里写内容 {%endmacro%}   下面引用 ...

  4. eclipse 修改默认的author

    1. 在eclipse.ini中添加 -vmargs -Duser.name={author name} 记得一定要在-vmargs之后,否则无效. 2. 设置eclipse参数 windows--& ...

  5. java自带的类压缩和下载,以及递归删除动态的文件(shiro项目中来的十)

    详见项目,不用借助于任何外在的jar包,通过jre自带的实现.

  6. UniCode 下 CString 转 char* 的方法(转)

    转自:http://blog.csdn.net/neverup_/article/details/5664733 今天进行文件操作时,将CString的GetBuffer()后直接倒到char数组后写 ...

  7. Jmeter_从jdbc请求的响应中获取参数做关联

    在之前的文章-参数关联中,留个一个小尾巴,这里补充一下 http://www.cnblogs.com/Zfc-Cjk/p/8295495.html 1:从sql表中将需要取的数据查出来 2:我们需要把 ...

  8. 【BZOJ4569】萌萌哒(并查集,倍增)

    [BZOJ4569]萌萌哒(并查集,倍增) 题面 BZOJ 题意: 有一个长度为\(n\)的数 给定\(m\)个限制条件 每次限制\(l1-r1\)与\(l2-r2\)是相同的 求出方案数 题解 如果 ...

  9. [ZJOI2007]时态同步

    题目描述 小Q在电子工艺实习课上学习焊接电路板.一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字1,2,3….进行标号.电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点 ...

  10. 【bzoj2151】种树

    Time Limit: 1000ms                     Memory Limit: 128MB Description A城市有一个巨大的圆形广场,为了绿化环境和净化空气,市政府 ...