首页 > 系统相关 >shell并发

shell并发

时间:2023-10-20 17:12:02浏览次数:54  
标签:文件 shell 管道 fd1 并发 描述符 1000

目录

1. for循环执行任务

  • 一个for循环1000次顺序执行1000次任务

    #!/bin/bash
    start_time=`date +%s` 	# 定义脚本运行的开始时间
     
    for ((i=1;i<=1000;i++))
    do
            sleep 1  	# sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
            echo 'success'$i;       
    done
     
    stop_time=`date +%s`    # 定义脚本运行的结束时间
    echo "TIME:`expr $stop_time - $start_time`"
    
    # 执行脚本输出结果
    success1
    success2
    success3
    ... ...
    success1000
    TIME:1000
    

脚本解析及问题:

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

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


2. 全并发执行任务

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

    #!/bin/bash
    start_time=`date +%s`   # 定义脚本运行的开始时间
    
    for ((i=1;i<=1000;i++))
    do
    {        sleep 1        # sleep 1用来模仿执行一条命令需要花费的时间(可以用真实命令来代替)
            echo 'success'$i;
    } &
    done
    wait
    
    stop_time=`date +%s`    # 定义脚本运行的结束时间
    echo "TIME:`expr $stop_time - $start_time`"
    
    # 执行脚本输出结果
    success726
    success697
    .... ...
    success979
    success963
    success981
    success987
    TIME:1
    

脚本解析及问题:

  • 解析:shell实现并发,1000个任务就会并发1000个线程,运行时间2s,比起方案一的1000s,已经非常快了

    { ... } & 把循环体的命令用&符号放入后台运行,一旦放入后台,就意味着 {} 里面的命令交给操作系统的一个线程处理

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

  • 问题:可以看出输出结果完全都是无序的,因为大家都是后台运行的,这个时候就是CPU随机运行了,所以并没有什么顺序。

    这样写确实可以实现并发,然后1000个任务就要并发1000个线程,这样对操作系统造成的压力非常大,它会随着并发任务数的增多,操作系统处理速度会慢甚至出现其他不稳定因素,就好比你在对nginx调优后,你认为你的nginx理论上最大可以支持1w并发,实际上呢,你的系统会随着高并发压力会不断攀升,处理速度会越来越慢


3. 并发控制

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

    • 管道文件fifo

      # 开两个终端
      
      # 终端1
      [root@localhost ~]# mkfifo /tmp/fd1  # 创建管道文件
      [root@localhost ~]# cat /tmp/fd1     # cat管道文件会在阻塞状态
      
      # 终端2
      [root@localhost ~]# echo "test" > /tmp/fd1 # 向管道文件输入内容
      
      # 终端1
      [root@localhost ~]# cat /tmp/fd1 
      test             # 读取到输入的文件,停止阻塞并输出内容
      [root@localhost ~]# cat /tmp/fd1     # cat管道文件会再次进入阻塞状态,等待管道文件输入内容
      

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

    • 文件描述符3 (3不是固定 可以写100等)

      创建有名管道文件exec 3<>/tmp/fd1,创建文件描述符3关联管道文件,这时候3这个文件描述符就拥有了管道的所有特性,还具有一个管道不具有的特性:无限存不阻塞,无限取不阻塞,而不用关心管道是否为空。也不用关心是否有内容写入引用文件描述符:&3可以执行n次echo >&3往管道里放入n段内容

    • read -u3

      读取文件描述符关联管道中的内容

    • 关闭文件描述符的读和写

      exec 3<&-

    #!/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<=100;i++))
    do
            echo >&3                   # &3代表引用文件描述符3,这条命令代表往管道里面放入了一个"令牌"
    done
     
    for ((i=1;i<=1000;i++))
    do
    read -u3                           # 代表从管道中读取一个令牌
    {
            sleep 1
            echo 'success'$i       
            echo >&3                   # 代表我这一次命令执行到最后,把令牌放回管道
    }&
    done
    
    wait  
     
    stop_time=`date +%s`
     
    echo "TIME:`expr $stop_time - $start_time`"
    exec 3<&-						  # 关闭文件描述符的读
    exec 3>&-						  # 关闭文件描述符的写
    
    # 执行脚本输出结果
    输出100条执行结果
    --------------停顿
    输出100条执行结果
    --------------停顿
    ...
    TIME:10
    

脚本解析:

  • 两个for循环

    • 第一个for循环10次,相当于放了100把钥匙
    • 第二个for循环1000次,相当于1000个人来执行任务
    • read -u3相当于取走一把钥匙。
    • {...}里面最后一行代码echo >&3执行完任务还钥匙。

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

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

标签:文件,shell,管道,fd1,并发,描述符,1000
From: https://www.cnblogs.com/ican97/p/17777542.html

相关文章

  • 它让你1小时精通RabbitMQ消息队列、且能扛高并发
    支持.NetCore(2.0及以上)与.NetFramework(4.5及以上)本文所述方案近期被江苏省某亿级数据量+高并发的政府"物联网"项目采用,获得圆满成功!!【目录】发送消息、获取消息、使用消息延时队列&死信队列展望RabbitMQ作为一款主流的消息队列工具早已广受欢迎。相比于其它......
  • 【PowerShell】Invoke-WebRequest和Invoke-RestMethod
    ##PublicfreeRestfulAPIURL ##https://documenter.getpostman.com/view/8854915/Szf7znEe#intro#Example01#--------------------------------------------------------------$url="https://cat-fact.herokuapp.com/facts/"$r=Invoke-WebReques......
  • shell脚本自动化实战
    Shell脚本自动化部署实战(二)原创 叶凡Jonas 软件测试成长之路 2023-09-0100:00 发表于上海收录于合集#UI自动化系列54个三丶shell语法4.程序结构2.循环结构说明:在上一篇博客中讲到了for循环,现在开始讲解while循环a)格式while[条件]do 命令done示例1:变量......
  • Shell-$(cd "$(dirname "$0")",pwd)
    #!/bin/bashBIN_FOLDER=$(cd"$(dirname"$0")";pwd)echo$BIN_FOLDERPROJECT_FOLDER=$(cd"$(dirname"$BIN_FOLDER")";pwd)echo$PROJECT_FOLDERPYTHON_BIN=$PROJECT_FOLDER/.venv/bin/pythonecho$PYTHON_BINBIN_FOLDER......
  • GIL全局解释器锁、互斥锁、线程队列、进程池和线程池的使用、多线程爬取网页、协程理
    进程和线程的比较进程的开销比线程的开销大很多进程之间的数据是隔离的,但是,线程之间的数据不隔离多个进程之间的线程数据不共享----->还是让进程通信(IPC)------->进程下的线程也通信了---->队列GIL全局解释器锁(重要理论)Python在设计之初就考虑到要在主循环中,同时只有一......
  • Lab4-事务与并发编程实现
              实验三存储过程与触发器实验目的:学习SQL语言进行编程的基本方法与技术,能够编写存储过程、触发器解决数据库需要处理的复杂问题。实验内容:1、 设计一个存储过程或者自定义函数,练习存储过程的设计方法。2、 设计触发器,理解触发器的工作原理与设计方法......
  • Linux shell编程学习笔记8:使用字符串
    一、前言字符串是大多数编程语言中最常用最有用的数据类型,这在Linuxshell编程中也不例外。本文讨论了LinuxShell编程中的字符串的三种定义方式的差别,以及字符串拼接、取字符串长度、提取字符串、查找子字符串等常用字符串操作,,以及反引号在echo和expr命令联合使用时的作用。二......
  • Linux shell编程学习笔记4:修改命令行提示符格式(内容和颜色)
    一、命令行提示符格式内容因shell类型而异Linux终端命令行提示符内容格式则因shell的类型而异,例如CoreLinux默认的shell是sh,其命令行提示符为黑底白字,内容为:tc@box:/$其中,tc为当前用户名,box为主机名,/为当前目录路径,$表示当前用户类型是普通用户 。 二、环境变量PS1存储了命令行提......
  • Linux-shell脚本使用ssh远程执行命令通过密码的方式登录
    1. sshpass简介sshpass是一个在非交互式ssh会话中自动输入密码的工具。它可以直接在命令行中指定密码,因此可以用于Shell脚本等自动化场景。在RedHat系统中,可以通过epel-release源安装sshpass。epel-release源是ExtraPackagesforEnterpriseLinux(EPEL)的缩写......
  • Linux-Shell 小数运算,四舍五入
    在shell中做小数运算,可以借助bc或者awk工具一、使用bc做小数运算,scale指定小数点位数1、加法运算(scale参数无效)#echo"5.999+5.001"|bc6.000#echo"5.111+5.1114"|bc10.2224运算结果小数点位数以加数中最大的为准 2、减法运算(scale参数无效)  同加法运算  运算......