Pow функция блокирует все потоки с ThreadPoolExecutor - PullRequest
1 голос
/ 20 июня 2019

Несмотря на то, что в первом примере с 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

, но программа перестает работать после «запуска pow ....», давая результат:

block 1
non 1
block 2
non 2
block 3
non 3
block 4
Starting pow....

Ответы [ 3 ]

1 голос
/ 20 июня 2019

Ваша реализация Python (вероятно, CPython) должна иметь Глобальную блокировку интерпретатора (GIL)

pow - это встроенная функция Python, которая не вызывает GIL при вызове,эффективная блокировка всех выполнений, пока он запущен и работает довольно долго.

Если вы хотите неблокировать, вместо этого используйте ProcessPoolExecutor.Отдельный процесс означает 2 отдельных GIL и не блокирование.У него точно такой же интерфейс.Он также имеет больше ограничений, так как требует, чтобы параметры были picklable (нет удобной разделяемой памяти, как с потоками)

Обратите внимание, что это вычисление, вероятно, никогда не заканчивается или заканчивается "ошибкой нехватки памяти": поскольку это сила между целыми числами с большим значением мощности, поэтому он не переполняется, как это делает поплавки, а вычисляет и вычисляет, создавая все большие и большие целые числа каждый раз

Расчетное количество цифрдля этого числа примерно:

>>> math.log10(363)*100000000000000
255990662503611.25

10 ^ 14 цифр, что больше, чем любой текущий компьютер может обрабатывать память, а также процессор.

1 голос
/ 20 июня 2019

Это из-за глобальной блокировки интерпретатора 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) занимает много времени, другой поток не имеет возможности работать во время него.

0 голосов
/ 20 июня 2019
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)

Время увеличивается в геометрической прогрессии

...