• [Shell] shell并发


    1. for循环并发执行 - 前台命令变后台进程

    shell中,后一个前台命令必须等待前一个前台命令执行完毕才能进行,这就是所谓的单线程程序。
    shell并没有真正意义上的多进程。而最简单的节省时间,达到“多线程”效果的办法,是将前台命令变成后台进程,这样一来就可以跳过前台命令的限制了。

    1.1 用法

    for((i=0;i<$num_tasks;i++))
    {
    }&
    done
    wait
    echo "done"

    1.2 问题

    以上的实现方法中,同时有num_tasks在后台运行,如果num_tasks个数非常大,那么很可能爆内存。那么如果协调内存和并发效率呢?

    1.3 解决方法

    控制同时启动的进程的个数。

    2. 控制并发执行的进程个数 - 管道 + 文件操作符实现队列

    2.1 Prerequisities

    2.1.1 管道文件

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

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

    有名管道特性:

    1. mkfifo /tmp/fd1 创建有名管道

       cat /tmp/fd1 显示管道中内容,如果管道内容为空,则阻塞

     

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

    在terminal 1 中执行以下命令,向管道中输入'test',由于没有读管道的操作,所以被阻塞了

    在terminal 2 中执行以下命令,读管道中的内容,于是terminal 1 结束了阻塞的状态。

    terminal 2 有了输出test,而terminal 1中命令终止。

    可以利用以上特性维护一个存令牌的队列,向其中写入一个令牌,被阻塞,只能这个令牌被读取之后,才可以结束阻塞的状态;

    如果用于并发进程的控制,以上特性可以用于保证并发的进程数是1。

    =====  但是,如果想每次并发多个进程要如何处理呢? =====

    可以看出管道特性中,阻碍“并发多个”的点在于 “写入一个即阻塞”,即限制了令牌队列的长度为1;

    那么如果可以不被阻塞,而可以由我们制定令牌长度,这样并发数就是我们指定的队列长度,也即队列中的初始写入的令牌数了。

    如果希望实现“不被阻塞”,那么可以采用“文件描述符”~

    2.1.2 文件描述符

    1. 简介

    文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。(百度百科)

    2. 文件描述符与管道文件关联

    exec [文件描述符fid] <> [管道文件fd]  

    该命令含义:创建文件描述符fid(非负整数,可以避开使用0 stdin, 1 stdout, 2 stderr),并关联(以读写方式 <> 打开)管道文件fd。

    此时文件描述符fid就拥有了管道的所有特性,还具有一个管道不具有的特性:无限存不阻塞,无限取不阻塞,而不用关心管道内是否为空,也不用关心是否有内容写入引用文件描述符&fid。

    可以执行n次echo >&fid 往管道里放入n个令牌。

    2.2 shell 指定并发数的实现

    2.2.1 实现逻辑

    设置这个队列的长度为可以并发执行的进程个数$num_para。

    (1) 先往这个管道中放置num_para个令牌;

    (2) 来了num_para个进程,取走了num_para个令牌;第num_para + 1个进程因为取不到令牌,被阻塞;

    (3) 最初取到令牌的进程中,有一个执行完了,释放令牌到管道中;

    (4) 管道中又有令牌了,一个被阻塞的进程可以获取该令牌,执行;

    (5) 循环(3)(4)直至所有进程执行完毕。

    2.2.2 实现代码

    start_time=`date +%s`                # 定义脚本运行的开始时间
    
    [ -e /tmp/fd1 ] || mkfifo /tmp/fd1   # 如果有名管道文件不存在,则创建
    
    exec 3<>/tmp/fd1                     # 创建文件描述符3, 并以可读<, 可写>的方式关联管道文件
                                         # 这时文件描述符3就有了有名管道文件的特性
    
    rm -rf /tmp/fd1                      # 关联后的文件描述符拥有管道文件的所有特性,所以这时候管道文件可以删除
                                         # 留下文件描述符来用就可以了
    
    for((i=1;i<=10;i++))                 # &3 引用文件描述符3,循环向管道中放入了10个令牌,支持并发数为10
    do
        echo >&3
    done
    
    for((i=1;i<=100;i++))
    do
    read -u3                             # 从管道中读取1个令牌
    {
        sleep 1                          # 模拟进程运行
        echo 'success'$i
        echo >&3                         # 该进程运行结束,将其令牌放回管道
    }&
    done
    wait
    
    
    stop_time=`date +%s`                 # 定义脚本运行的结束时间
    
    echo "Time: `expr $stop_time - $start_time`"   # 获取执行100条进程总的运行时间
    
    exec 3<&-                            # 关闭文件描述符的读 
    exec 3>&-                            # 关闭文件描述符的写

    输出:

    success1...省略

    参考链接:

    1. shell队列实现线程并发控制:https://www.cnblogs.com/chenjiahe/p/6268853.html

    2. shell实现多线程笔记:https://blog.51cto.com/mochaming/1279864

    3. exec操作文件描述符:http://blog.sina.com.cn/s/blog_7099ca0b0100nby8.html

    4. 文件描述符:https://blog.csdn.net/Captain_MXD/article/details/52153233

  • 相关阅读:
    vmware fusion vmware tools灰的
    Android Studio Flutter开发测试碰到的问题 Running Gradle task assembleDebug
    eclipse 连接mysql表生成实体类
    png2ico小工具使用
    ionic+vue+capacitor系列笔记常见报错以及解决
    Strapi入门记01创建项目,账户,测试表,测试接口
    ionic+vue+capacitor系列笔记01项目初始化
    ionic+vue+capacitor系列笔记03项目使用Native插件
    ionic+vue+capacitor系列笔记02项目中集成Capacitor,添加android,ios平台,真机运行项目
    微信图片缓存中的 dat 文件处理
  • 原文地址:https://www.cnblogs.com/shiyublog/p/13561100.html
Copyright © 2020-2023  润新知