pow 函数使用 ThreadPoolExecutor 阻塞所有线程
pow function blocking all threads with ThreadPoolExecutor
尽管有关 ThreadPoolExecutor 的第一个示例使用了 pow 函数 (https://docs.python.org/3/library/concurrent.futures.html),但使用 pow 似乎会在调用时异常阻塞所有线程。请参阅下面的代码。
from concurrent.futures import ThreadPoolExecutor
import time
executor = ThreadPoolExecutor(max_workers=16)
def blocking():
i = 0
while True:
time.sleep(1)
i+=1
print("block",i)
if i>3:
print("Starting pow....")
break
block= pow(363,100000000000000)
return True
def non_blocking():
i = 0
while True:
time.sleep(1)
i+=1
print("non",i)
return True
f1 = executor.submit(blocking)
f2 = executor.submit(non_blocking)
我期望输出:
block 1
non 1
block 2
non 2
block 3
non 3
block 4
Starting pow....
non 4
non 5
non 6
non 7
non 8
但程序在 "starting pow...." 后停止 运行 给出结果:
block 1
non 1
block 2
non 2
block 3
non 3
block 4
Starting pow....
pow(363,100000000000000)
如果计算量很大。它需要时间来处理并给出结果
简单计算:
>>> import time
>>> def test(power):
... start = time.time()
... pow(363, power)
... return time.time() - start
...
>>> for power in range(1,11):
... print(test(pow(10, power)))
...
6.198883056640625e-06
2.1457672119140625e-06
3.0517578125e-05
0.0009307861328125
0.0421295166015625
1.7541508674621582 #This is a (363, 1000000)
时间呈指数增长
您的 python 实现(可能是 CPython)必须具有 Global Interpreter Lock (GIL)
pow
是一个原生的 Python 函数,它在被调用时不会释放 GIL,在 运行 和 运行 时有效地阻止所有执行而
如果您想要 non-blocking,请改用 ProcessPoolExecutor
。一个单独的进程意味着 2 个独立的 GIL 并且没有阻塞。它具有完全相同的界面。它也有更多的限制,因为它要求参数是 picklable(没有像线程那样方便的共享内存)
请注意,此计算可能永远不会结束或以 "out of memory error" 结束:因为它是具有 big 幂值的整数之间的幂,所以它不会溢出浮点数可以,但计算和计算,每次都会创建越来越大的整数
这个数字的估计位数大致是:
>>> math.log10(363)*100000000000000
255990662503611.25
10^14 位数字超过了当前任何计算机的内存处理能力,而且 CPU-wise.
这是因为 Python 的全局解释器锁 (GIL)。
如果您查看函数的字节码:
import time
def blocking():
i = 0
while True:
time.sleep(1)
i+=1
print("block",i)
if i>3:
print("Starting pow....")
break
block= pow(363,100000000000000)
return True
import dis
dis.dis(blocking)
看起来像这样:
4 0 LOAD_CONST 1 (0)
2 STORE_FAST 0 (i)
5 4 SETUP_LOOP 50 (to 56)
6 >> 6 LOAD_GLOBAL 0 (time)
8 LOAD_METHOD 1 (sleep)
10 LOAD_CONST 2 (1)
12 CALL_METHOD 1
14 POP_TOP
7 16 LOAD_FAST 0 (i)
18 LOAD_CONST 2 (1)
20 INPLACE_ADD
22 STORE_FAST 0 (i)
8 24 LOAD_GLOBAL 2 (print)
26 LOAD_CONST 3 ('block')
28 LOAD_FAST 0 (i)
30 CALL_FUNCTION 2
32 POP_TOP
9 34 LOAD_FAST 0 (i)
36 LOAD_CONST 4 (3)
38 COMPARE_OP 4 (>)
40 POP_JUMP_IF_FALSE 6
10 42 LOAD_GLOBAL 2 (print)
44 LOAD_CONST 5 ('Starting pow....')
46 CALL_FUNCTION 1
48 POP_TOP
11 50 BREAK_LOOP
52 JUMP_ABSOLUTE 6
54 POP_BLOCK
12 >> 56 LOAD_GLOBAL 3 (pow)
58 LOAD_CONST 6 (363)
60 LOAD_CONST 7 (100000000000000)
62 CALL_FUNCTION 2
64 STORE_FAST 1 (block)
13 66 LOAD_CONST 8 (True)
68 RETURN_VALUE
您会注意到 pow
调用作为单个字节码指令发生在 62。 GIL 只能在字节码之间传递,因此由于 pow(363,100000000000000)
需要很长时间才能执行,其他线程在执行期间没有机会 运行。
尽管有关 ThreadPoolExecutor 的第一个示例使用了 pow 函数 (https://docs.python.org/3/library/concurrent.futures.html),但使用 pow 似乎会在调用时异常阻塞所有线程。请参阅下面的代码。
from concurrent.futures import ThreadPoolExecutor
import time
executor = ThreadPoolExecutor(max_workers=16)
def blocking():
i = 0
while True:
time.sleep(1)
i+=1
print("block",i)
if i>3:
print("Starting pow....")
break
block= pow(363,100000000000000)
return True
def non_blocking():
i = 0
while True:
time.sleep(1)
i+=1
print("non",i)
return True
f1 = executor.submit(blocking)
f2 = executor.submit(non_blocking)
我期望输出:
block 1
non 1
block 2
non 2
block 3
non 3
block 4
Starting pow....
non 4
non 5
non 6
non 7
non 8
但程序在 "starting pow...." 后停止 运行 给出结果:
block 1
non 1
block 2
non 2
block 3
non 3
block 4
Starting pow....
pow(363,100000000000000)
如果计算量很大。它需要时间来处理并给出结果
简单计算:
>>> import time
>>> def test(power):
... start = time.time()
... pow(363, power)
... return time.time() - start
...
>>> for power in range(1,11):
... print(test(pow(10, power)))
...
6.198883056640625e-06
2.1457672119140625e-06
3.0517578125e-05
0.0009307861328125
0.0421295166015625
1.7541508674621582 #This is a (363, 1000000)
时间呈指数增长
您的 python 实现(可能是 CPython)必须具有 Global Interpreter Lock (GIL)
pow
是一个原生的 Python 函数,它在被调用时不会释放 GIL,在 运行 和 运行 时有效地阻止所有执行而
如果您想要 non-blocking,请改用 ProcessPoolExecutor
。一个单独的进程意味着 2 个独立的 GIL 并且没有阻塞。它具有完全相同的界面。它也有更多的限制,因为它要求参数是 picklable(没有像线程那样方便的共享内存)
请注意,此计算可能永远不会结束或以 "out of memory error" 结束:因为它是具有 big 幂值的整数之间的幂,所以它不会溢出浮点数可以,但计算和计算,每次都会创建越来越大的整数
这个数字的估计位数大致是:
>>> math.log10(363)*100000000000000
255990662503611.25
10^14 位数字超过了当前任何计算机的内存处理能力,而且 CPU-wise.
这是因为 Python 的全局解释器锁 (GIL)。
如果您查看函数的字节码:
import time
def blocking():
i = 0
while True:
time.sleep(1)
i+=1
print("block",i)
if i>3:
print("Starting pow....")
break
block= pow(363,100000000000000)
return True
import dis
dis.dis(blocking)
看起来像这样:
4 0 LOAD_CONST 1 (0)
2 STORE_FAST 0 (i)
5 4 SETUP_LOOP 50 (to 56)
6 >> 6 LOAD_GLOBAL 0 (time)
8 LOAD_METHOD 1 (sleep)
10 LOAD_CONST 2 (1)
12 CALL_METHOD 1
14 POP_TOP
7 16 LOAD_FAST 0 (i)
18 LOAD_CONST 2 (1)
20 INPLACE_ADD
22 STORE_FAST 0 (i)
8 24 LOAD_GLOBAL 2 (print)
26 LOAD_CONST 3 ('block')
28 LOAD_FAST 0 (i)
30 CALL_FUNCTION 2
32 POP_TOP
9 34 LOAD_FAST 0 (i)
36 LOAD_CONST 4 (3)
38 COMPARE_OP 4 (>)
40 POP_JUMP_IF_FALSE 6
10 42 LOAD_GLOBAL 2 (print)
44 LOAD_CONST 5 ('Starting pow....')
46 CALL_FUNCTION 1
48 POP_TOP
11 50 BREAK_LOOP
52 JUMP_ABSOLUTE 6
54 POP_BLOCK
12 >> 56 LOAD_GLOBAL 3 (pow)
58 LOAD_CONST 6 (363)
60 LOAD_CONST 7 (100000000000000)
62 CALL_FUNCTION 2
64 STORE_FAST 1 (block)
13 66 LOAD_CONST 8 (True)
68 RETURN_VALUE
您会注意到 pow
调用作为单个字节码指令发生在 62。 GIL 只能在字节码之间传递,因此由于 pow(363,100000000000000)
需要很长时间才能执行,其他线程在执行期间没有机会 运行。