concurrent.futures.ThreadPoolExecutor() 为什么线程在转到下一行之前等待完成?

concurrent.futures.ThreadPoolExecutor() why threads wait for completion before going to next line?

尝试下面的代码。

import concurrent.futures
import time

def do_it():
    with concurrent.futures.ThreadPoolExecutor() as my_executor:
        t1 = my_executor.submit(doing, 3)
        ret_value = t1.result()
        t2 = my_executor.submit(some_func)
        return f"doing return is {ret_value}"


def doing(num):
    print(f"Calculating Square for {num}")
    return num*num


def some_func():
    print("sleep for 6 sec")
    time.sleep(6)
    print("done sleeping 6 secs")


start = time.perf_counter()
print(do_it())
finish = time.perf_counter()
print(f"total time {finish-start}")

低于输出:

Calculating Square for 3
sleep for 6 sec
done sleeping 6 secs
doing return is 9
total time 6.0060749100002795

但我期待(并且想要):

Calculating Square for 3
sleep for 6 sec
doing return is 9
total time <time much much less than 6>
<then after 6 sec>
done sleeping 6 secs

我想要 t1 Asap 的 return 值,让 t2 继续。我怎样才能实现它。感谢您的帮助。

你在这里写了什么:

    t1 = my_executor.submit(doing, 3)
    ret_value = t1.result()
    t2 = my_executor.submit(some_func)

使两个函数(doingsome_func)按顺序 运行ning 而不是并发,因为您使用 .result() 显式阻塞并等待第一个函数的值在启动第二个之前。

如果你想同时运行这两个函数,那么你必须在等待它们之前提交它们:

def do_it():
    with concurrent.futures.ThreadPoolExecutor() as my_executor:
        t1 = my_executor.submit(doing, 3)
        t2 = my_executor.submit(some_func)
        ret_value = t1.result()
        return f"doing return is {ret_value}"

这里,t1t2运行并发,他们(几乎)同时提交。然后你等待 t1 通过 .result() 的结果和 return 它的值,这就是你可能想要的。

但是,如果您需要等待两个函数之间的第一个可用结果,您可以使用 wait 函数或 as_completed 函数,看看 documentation学习如何使用它们。


编辑

with语句打开一个上下文管理器,在继续之前调用执行器的.shutdown()。此方法在 returning 之前等待所有期货完成,因此当 t1 和 t2 完成时 do_it() 仅 returns。

如果你想returnt2一启动,将executor作为参数传递,避免在这个函数中调用.shutdown()

import concurrent.futures
import time

def do_it(executor):
    ret_value = doing(3)
    t2 = executor.submit(some_func)
    return f"doing return is {ret_value}"

def doing(num):
    print(f"Calculating Square for {num}")
    return num*num

def some_func():
    print("sleep for 6 sec")
    time.sleep(6)
    print("done sleeping 6 secs")

with concurrent.futures.ThreadPoolExecutor() as executor:
    start = time.perf_counter()
    print(do_it(executor))
    finish = time.perf_counter()
    print(f"total time {finish-start}")

你说t2需要t1的值,所以那些调用不能并发,你可以串行执行t1。

另外请注意,在 t1 完成后立即在 do_it 函数内提交 t2 并没有实际意义。你可以提交 t2 after do_it has returned,这更符合逻辑也更简单:

import concurrent.futures
import time


def do_it():
    ret_value = doing(3)
    return f"doing return is {ret_value}"

def doing(num):
    print(f"Calculating Square for {num}")
    return num*num

def some_func():
    print("sleep for 6 sec")
    time.sleep(6)
    print("done sleeping 6 secs")

with concurrent.futures.ThreadPoolExecutor() as executor:
    start = time.perf_counter()
    print(do_it())
    executor.submit(some_func)
    finish = time.perf_counter()
    print(f"total time {finish-start}")

这并没有给出你想要的确切输出(两个打印语句交错),但这无关紧要,结果是一样的,t2 在 t1 完成后立即启动。