7

if在 bash 脚本中多线程独立语句的正确方法是什么?是否最好将包含在表达式中或之后的&后代码?if

对于after 表达式,如果它包含大量代码&,则根据需要继续线程化是有意义的。if但是一行代码也应该以&?

表达式后:

if [ expression ] &
  then
    #task
fi

任务结束后:

if [ expression ]
  then
    #task &
fi


想象一下 3 个if语句,它们都执行彼此独立的任务,执行如何在不同位置的情况下工作&?据我了解,如果放在表达式之后,所有 3 个表达式(基本上)同时开始,这 3 个任务也是如此。

#Thread 1         #Thread 2         #Thread 3
if [ expr1 ] &    if [ expr2 ] &    if [ expr3 ] &
  then              then              then
    task1            task2            task3
fi                fi                fi
wait

如果放置在任务代码之后,if将评估第一个任务,并且只有在第一个任务开始时才会if评估第二个任务。这些任务比同时进行的更交错。

#Thread 1         #Thread 2         #Thread 3
if [ expr1 ]
  then
    task1 &       if [ expr2 ]
fi                  then
                      task2 &       if [ expr3 ]
                  fi                  then
                                        task3 &
                                    fi
wait

表达式不能组合以在if诸如以下的内部进行线程化:

if [ combined expression ]
  then
    #task1 &
    #task2 &
    #task3 &
fi
4

4 回答 4

6

如果您希望每个if条件在其各自的“线程”(实际上是子外壳进程)的上下文中执行,那么我认为要做的事情是&fi每个if. 然后每个if表达式的评估以及条件代码将完全发生在它自己的“线程”的上下文中。

例如:

#/bin/bash

if [ 1 ] 
  then
    for i1 in {1..3}; do echo $i1; sleep 1; done
fi &
if [ 1 ]
  then
    for i2 in {a..c}; do echo $i2; sleep 1; done
fi &
wait

每个“线程”的输出按预期交错:

1
a
2
b
3
c

请注意,在所有使用 的情况下&,这些实际上是进程(使用 创建fork())而不是线程(使用创建pthread_create())。请参阅Bash 中的多线程。您可以通过创建一个变量来测试它,例如n=0在“线程”启动之前。然后在一个线程中递增 n 并在所有线程中回显 $n。您会看到每个“线程”都有自己的 n 副本 - n 在递增和非递增线程中将具有不同的值。 fork()创建一个新的流程副本(包括变量的独立副本);pthread_create()没有。

于 2013-10-18T19:47:13.310 回答
5

将 放在&后面if将仅作为条件表达式评估的背景;[实际上是test命令的别名,这将是背景,而不是任务。

&放在任务之后将使任务成为后台。任务交错而不是同时的程度取决于评估所需的相对时间expr1与执行所需的时间相比task1

请参阅以下脚本:

#!/bin/bash

if [ 1 ] &
then
    sleep 10 
fi
if [ 1 ] &
then
    sleep 10 
fi
if [ 1 ] &
then
    sleep 10 
fi
wait

运行需要 30 秒

$ time . test.sh 
[3]   Done                    [ 1 ]
[3]   Done                    [ 1 ]
[3]   Done                    [ 1 ]

real    0m30.015s
user    0m0.003s
sys 0m0.012s

你会看到后台任务是[ 1 ],不是sleep。请注意,正如@chepner 所解释的那样,这是没有意义的。

现在是另一种情况:

#!/bin/bash

if [ 1 ] 
then
    sleep 10 &
fi
if [ 1 ] 
then
    sleep 10 &
fi
if [ 1 ] 
then
    sleep 10 &
fi
wait

给出:

[2]   Done                    sleep 10
[3]   Done                    sleep 10
[4]-  Done                    sleep 10

real    0m10.197s
user    0m0.003s
sys 0m0.014s

只需10秒;所有这些sleeps都是同时进行的,因为它们是背景的。

@DigitalTrauma 描述的最后一个选项:

#!/bin/bash

if [ 1 ] 
then
    sleep 10 
fi &
if [ 1 ]
then
    sleep 10 
fi &
if [ 1 ] 
then
    sleep 10
fi &
wait

然后整个if陈述是背景的,即评估exprN和评估taskN

[3]   Done                    if [ 1 ]; then
    sleep 10;
fi
[4]   Done                    if [ 1 ]; then
    sleep 10;
fi
[5]   Done                    if [ 1 ]; then
    sleep 10;
fi

real    0m10.017s
user    0m0.003s
sys 0m0.013s

给出预期的 10 秒运行时间

于 2013-10-18T19:41:09.510 回答
4

相比

if false
then
  echo "condition true"
fi

if false &
then
  echo "condition ... true?"
fi

&以状态 0 退出的命令立即退出,因此if无论后台进程最终返回什么,都会执行分支。if将无条件成功的命令放在语句的命令列表中是没有意义的。您必须让它完成,以便可以正确(有条件地)执行主体。

于 2013-10-18T20:02:45.983 回答
0

到目前为止的答案依赖于使用产卵 3 并等待所有 3 完成后再重新开始。

bash 中的“更好”方式是使用这样的jobs命令

jobs|wc -l然后像这样用一个小的睡眠命令循环

(未经测试的代码)

 while [  $COUNTER -lt 10 ]; do
if [[$(jobs | wc-l`) < 3 ]] then 

    expression &
    fi
    sleep 10;

done

这将允许填充所有 3 个“线程”,而无需等待所有 3 个线程都完成后再重新填充

于 2013-10-19T00:13:39.020 回答