什么行不通

什么行不通

我正在执行下面的脚本

LOGDIR=~/curl_result_$(date |tr ' :' '_')

mkdir $LOGDIR

for THREADNO in $(seq 20)
do
for REQNO in $(seq 20)
do
 time curl --verbose -sS  http://dummy.restapiexample.com/api/v1/create --trace-ascii ${LOGDIR}/trace_${THREADNO}_${REQNO} -d @- <<EOF >> ${LOGDIR}/response_${THREADNO} 2>&1
 {"name":"emp_${THREADNO}_${REQNO}_$(date |tr ' :' '_')","salary":"$(echo $RANDOM%100000|bc)","age":"$(echo $RANDOM%100000|bc)"}
EOF
echo -e "\n-------------------------------" >> ${LOGDIR}/response_${THREADNO}
done 2>&1 | grep real > $LOGDIR/timing_${THREADNO} &
done

一段时间后,如果我检查没有 bash 进程,它会显示 20(不是 1 或 21)

ps|grep bash|wc -l

问题是因为我没有使用括号“()”来包围内部循环,所以不应生成新的 shell 进程。我想避免在 CPU 使用率接近 100% 时创建新的 shell。我不知道这是否重要,但我正在使用 Cygwin。

答案1

因为您已将循环通过管道传输到grep,所以它必须在子 shell 中运行。这个有提到在 Bash 手册中:

管道中的每个命令都在其自己的子 shell 中执行,这是一个单独的进程(请参阅命令执行环境

可以通过以下方式避免这种情况lastpipe外壳选项为了最终的管道中的命令,但不是其他任何命令。无论如何,您已将整个管道置于后台,这创建一个子shell。

这是没有办法解决的。您所做的事情本质上确实需要单独的 shell 进程才能工作:即使忽略管道,创建后台进程也需要创建一个进程。

如果您的问题是 CPU 使用率,那么这是由于同时运行所有内容造成的。如果删除&后的grep,所有命令将按顺序运行,而不是同时运行。仍然会创建子 shell(为管道),但在这种情况下,这些本身并不是主要问题。如果您需要它们同时运行,那么增加的 CPU 使用率就是您所选择的权衡。

答案2

什么行不通

Bash不支持线程并行,只支持多进程并行。

Bash 无法在不生成子进程的情况下在后台运行 for 循环(或管道)。令我惊讶的是,bash 进程有 20 个,而不是 21 个。

我对 Cygwin 一无所知。


备择方案

如果您对 Python 比较熟悉,我建议您使用 Plumbum 库进行调用。Python 支持线程,这将使一切变得更容易。

这是您的代码,经过重写和测试:

from datetime import datetime
import json
import random
from plumbum import cmd as c
from threading import Thread

def now():
    return datetime.now().strftime("%Y-%m-%d %H_%M_%S")

logdir = f"~/curl_result_{now()}"

def curl(threadno, reqno):
    args1 = "--verbose -sS http://dummy.restapiexample.com/api/v1/create --trace-ascii".split()
    args2 = [f"{logdir}_{threadno}_{reqno}", "-d", "@-"]
    content = json.dumps({
        "name": f"{logdir}/trace_{threadno}_{reqno}_{now()}",
        "salary": random.randrange(100_000),
        "age": random.randrange(100_000) ,
    })
    call = c.echo[content] | c.curl[(*args1, *args2)] >> f"{logdir}/response_{threadno}"
    print(call)
    # call()

def curl_batch(threadno):
    for reqno in range(20):
        curl(threadno, reqno)

# Start 20 threads
threadList = []
for threadno in range(20):
    t = Thread(target=curl_batch, args=(threadno,))
    t.start()
    threadList.append(t)

# Wait for every thread
for thread in threadList:
    thread.join()

享受 Python 的灵活性;)

相关内容