【发布时间】:2014-08-03 15:24:00
【问题描述】:
我正在使用 Python 2 subprocess 和 threading 线程来获取标准输入,使用二进制文件 A、B 和 C 对其进行处理,并将修改后的数据写入标准输出。
这个脚本(我们称之为:A_to_C.py)非常慢,我想学习如何修复它。
大致流程如下:
A_process = subprocess.Popen(['A', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
produce_A_thread = threading.Thread(target=produceA, args=(sys.stdin, A_process.stdin))
B_process = subprocess.Popen(['B', '-'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
convert_A_to_B_thread = threading.Thread(target=produceB, args=(A_process.stdout, B_process.stdin))
C_process = subprocess.Popen(['C', '-'], stdin=subprocess.PIPE)
convert_B_to_C_thread = threading.Thread(target=produceC, args=(B_process.stdout, C_process.stdin))
produce_A_thread.start()
convert_A_to_B_thread.start()
convert_B_to_C_thread.start()
produce_A_thread.join()
convert_A_to_B_thread.join()
convert_B_to_C_thread.join()
A_process.wait()
B_process.wait()
C_process.wait()
这个想法是标准输入进入A_to_C.py:
-
A二进制文件处理一大块标准输入并使用函数produceA创建A-输出。 -
B二进制文件处理A的标准输出块,并通过函数produceB创建B-输出。 -
C二进制文件通过函数produceC处理B的标准输出块,并将C-output 写入标准输出。
我使用 cProfile 进行了分析,并且该脚本中的几乎所有时间似乎都花在了获取线程锁上。
例如,在测试 417s 作业中,416s(>99% 的总运行时间)用于获取线程锁:
$ python
Python 2.6.6 (r266:84292, Nov 21 2013, 10:50:32)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pstats
>>> p = pstats.Stats('1.profile')
>>> p.sort_stats('cumulative').print_stats(10)
Thu Jun 12 22:19:07 2014 1.profile
1755 function calls (1752 primitive calls) in 417.203 CPU seconds
Ordered by: cumulative time
List reduced from 162 to 10 due to restriction <10>
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.020 0.020 417.203 417.203 A_to_C.py:90(<module>)
1 0.000 0.000 417.123 417.123 A_to_C.py:809(main)
6 0.000 0.000 416.424 69.404 /foo/python/2.7.3/lib/python2.7/threading.py:234(wait)
32 416.424 13.013 416.424 13.013 {method 'acquire' of 'thread.lock' objects}
3 0.000 0.000 416.422 138.807 /foo/python/2.7.3/lib/python2.7/threading.py:648(join)
3 0.000 0.000 0.498 0.166 A_to_C.py:473(which)
37 0.000 0.000 0.498 0.013 A_to_C.py:475(is_exe)
3 0.496 0.165 0.496 0.165 {posix.access}
6 0.000 0.000 0.194 0.032 /foo/python/2.7.3/lib/python2.7/subprocess.py:475(_eintr_retry_call)
3 0.000 0.000 0.191 0.064 /foo/python/2.7.3/lib/python2.7/subprocess.py:1286(wait)
我的threading.Thread 和/或subprocess.Popen 安排有什么问题导致此问题?
【问题讨论】:
-
这可能是由
.join()调用引起的,如here 所写 -
您是否尝试过将代码纯粹作为子进程编写,没有线程?
-
您误读了分析器输出。 cProfile 发现您的 主线程 必须等待
.join()调用完成。416.424的总时间只是主线程等待工作线程完成工作。您的程序的其余部分几乎没有工作,因为您受 I/O 限制(例如,等待外部进程完成它们的工作)。 这是怎么回事? -
问题是,如果我用
A|B|C和awk来模拟生产功能所做的一些事情,那么完成任务所需的时间就会大大减少。这种基于 Python 的线程方法似乎比基于 shell 或其他方法消耗更多的时间。问题是 Python 脚本提供了一些使用基于 shell 的方法难以模拟的功能,但性能使脚本无法使用;我的目标是弄清楚为什么这会花费这么多时间,或者找到一种替代方法来管理标准 I/O 流。 -
你能发布完整的代码吗?这是缺少
produceAproduceBproduceC函数。 (或者至少,用通过的虚拟函数来展示它)
标签: python multithreading subprocess python-multithreading