如果我明白你想做什么,你基本上有很多适合多线程的工作,除了有一些 CPU 密集型工作。所以你的想法是在多个子进程中创建多个线程池,从而减少 GIL 争用。当然,在任何给定的子进程中,CPU 密集型代码只会串行执行(假设它是 Python 字节码),因此这不是一个完美的解决方案。
一种方法是创建一个非常大的多处理池(大于您拥有的内核数)。您可以创建的进程数量是有限的,而且创建它们的成本很高。但由于大部分时间他们将等待 I/O 完成,因此 I/O 部分将并发执行。
更好的方法是创建一个多处理池,其执行程序可以与其他所需参数一起传递给多线程池工作函数。这是你计划做的事情的倒置。当 worker 函数有一个 CPU 密集型工作要执行时,它可以将该工作提交给传递的多处理池执行器并阻塞返回的结果。通过这种方式,您可以获得最佳的并行性,您可以在给定您拥有的内核数量的情况下实现。这是我的建议。
但是如果你想坚持你最初的想法,也许像下面这样的东西可能会奏效:
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed
from multiprocessing import Queue
from queue import Empty
def init_pool_processes(q):
global queue, thread_pool_executor
queue = q
thread_pool_executor = ThreadPoolExecutor(10) # or some appropriate pool size
def thread_worker(x):
import time
# Do something with x
...
time.sleep(.1) # simulate time taken
return x # Just for demo purposes
def process_worker(y):
# This results in some number of threadpool jobs:
futures = [thread_pool_executor.submit(thread_worker, y + i) for i in range(5)]
for future in as_completed(futures):
queue.put(future.result())
if __name__ == '__main__':
results = []
def get_results(result):
try:
while True:
result = queue.get_no_wait()
results.append(result)
except Empty:
pass
input_args = (100, 200, 300, 400, 500)
queue = Queue()
with ProcessPoolExecutor(initializer=init_pool_processes, initargs=(queue,)) as executor:
futures = [executor.submit(process_worker, input_arg) for input_arg in input_args]
for future in as_completed(futures):
# Every time a job submitted to the process pool completes we can
# look for more results:
try:
while True:
result = queue.get_nowait()
results.append(result)
except Empty:
pass
print(results)
印刷:
[102, 201, 101, 203, 103, 202, 200, 100, 104, 204, 504, 301, 404, 502, 304, 403, 302, 501, 503, 500, 402, 303, 401, 300, 400]