bash脚本(子流程)的多线程信号灯

时间:2018-10-04 02:16:41

标签: bash

信号量式结构是否有任何方式/二进制?例如。为了在我们遍历文件目录时运行固定数量的(后台)子流程(在这里使用单词“ sub-process”而不是“ thread”,因为在我的bash命令中使用附加的& “多线程”(但可以接受任何更方便的建议)。

我的实际用例是尝试在CentOS 7上使用名为bcp的二进制文件将一组(可变大小的)TSV文件写入远程MSSQL Server DB,并且观察到似乎存在问题。运行太多线程时的程序。例如。像

for filename in $DATAFILES/$TARGET_GLOB; do

    if [ ! -f $filename ]; then
        echo -e "\nFile $filename not found!\nExiting..."
        exit 255
    else
        echo -e "\nImporting $filename data to $DB/$TABLE"
    fi

    echo -e "\nStarting BCP export threads for $filename"
    /opt/mssql-tools/bin/bcp "$TABLE" in "$filename" \
        $TO_SERVER_ODBCDSN \
        -U $USER -P $PASSWORD \
        -d $DB \
        $RECOMMEDED_IMPORT_MODE \
        -t "\t" \
        -e ${filename}.bcperror.log &
done
# collect all subprocesses at the end
wait

以不受限制的方式立即为每个文件所有启动一个新子流程的

似乎崩溃了每个子流程。希望查看是否在循环中添加类似信号量的结构以锁定将被旋转的子进程数量会有所帮助。例如。诸如此类(在此处使用一些非bash类的伪代码)

sem = Semaphore(locks=5)
for filename in $DATAFILES/$TARGET_GLOB; do

    if [ ! -f $filename ]; then
        echo -e "\nFile $filename not found!\nExiting..."
        exit 255
    else
        echo -e "\nImporting $filename data to $DB/$TABLE"
    fi

    sem.lock()
    <same code from original loop>
    sem.unlock()

done
# collect all subprocesses at the end
wait

如果这样的事情是可能的,或者如果这是现有最佳实践解决方案的常见问题(对于bash编程我还是很陌生),建议将不胜感激。

3 个答案:

答案 0 :(得分:1)

遵循@Mark Setchell的建议,使用GNU Parallel将循环(在模拟cron环境中(请参见https://stackoverflow.com/a/2546509/8236733)替换为

bcpexport() {
    filename=$1
    TO_SERVER_ODBCDSN=$2
    DB=$3 
    TABLE=$4 
    USER=$5
    PASSWORD=$6
    RECOMMEDED_IMPORT_MODE=$7
    DELIMITER=$8 # DO NOT use format like "'\t'", nested quotes seem to cause hard-to-catch error
    <same code from original loop>
}
export -f bcpexport
parallel -j 10 bcpexport \
    ::: $DATAFILES/$TARGET_GLOB \
    ::: "$TO_SERVER_ODBCDSN" \
    ::: $DB \
    ::: $TABLE \
    ::: $USER \
    ::: $PASSWORD \
    ::: $RECOMMEDED_IMPORT_MODE \
    ::: $DELIMITER

一次最多可运行10个线程,其中$DATAFILES/$TARGET_GLOB是全局字符串,用于返回所需目录中的所有文件。 (例如,“ $ storagedir / tsv / *。tsv”)(并将剩余的固定参数与该glob返回的每个元素相加,作为所示的剩余并行输入)($TO_SERVER_ODBCDSN变量实际上是“ -D -S <some ODBC DSN>”,因此需要添加引号以作为单个arg传递。因此,如果$DATAFILES/$TARGET_GLOB全局文件返回文件A,B,C ...,我们最终将运行命令

bcpexport A "$TO_SERVER_ODBCDSN" $DB ...
bcpexport B "$TO_SERVER_ODBCDSN" $DB ...
bcpexport C "$TO_SERVER_ODBCDSN" $DB ...
...

并行。

是使用parallel的另一个好处。
  

GNU并行确保命令的输出与顺序运行命令所获得的输出相同。

答案 1 :(得分:0)

使用&

示例代码

#!/bin/bash
xmms2 play &
sleep 5
xmms2 next &
sleep 1
xmms2 stop

答案 2 :(得分:0)

这并不完全等效,但是您可以使用xargs一次启动给定数量的进程:

-P max-procs, --max-procs=max-procs
      Run  up  to max-procs processes at a time; the default is 1.  If
      max-procs is 0, xargs will run as many processes as possible  at
      a  time.   Use the -n option or the -L option with -P; otherwise
      chances are that only one exec will be  done.   While  xargs  is
      running,  you  can send its process a SIGUSR1 signal to increase
      the number of commands to run simultaneously, or  a  SIGUSR2  to
      decrease  the  number.   You  cannot decrease it below 1.  xargs
      never terminates its commands; when asked to decrease, it merely
      waits  for  more  than  one existing command to terminate before
      starting another.

类似的东西:

printf "%s\n" $DATAFILES/$TARGET_GLOB |
  xargs -d '\n' -I {} --max-procs=5 bash -c '
    filename=$1
    if [ ! -f $filename ]; then
        echo -e "\nFile $filename not found!\nExiting..."
        exit 255
    else
        echo -e "\nImporting $filename data to $DB/$TABLE"
    fi

    echo -e "\nStarting BCP export threads for $filename"
    /opt/mssql-tools/bin/bcp "$TABLE" in "$filename" \
        $TO_SERVER_ODBCDSN \
        -U $USER -P $PASSWORD \
        -d $DB \
        $RECOMMEDED_IMPORT_MODE \
        -t "\t" \
        -e ${filename}.bcperror.log
  ' _ {}

您需要预先导出TABLETO_SERVER_ODBCDSNUSERPASSWORDDBRECOMMEDED_IMPORT_MODE变量,以便它们在xargs开始的过程中可用。或者,您可以在此处将使用bash -c运行的命令放在单独的脚本中,然后将变量放入该脚本中。