我正在执行下面的脚本
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 的灵活性;)