Как такой код выполняется параллельно, несмотря на GIL?

Это все о Python2.7

У меня следующий вопрос:

from futures import ThreadPoolExecutor

def test():

    while True:

        i = 4


executor = ThreadPoolExecutor(max_workers=2)

executor.submit(test)

executor.submit(test)

Поэтому, когда я запускаю такой код на двухъядерной машине, оба ядра становятся занятыми. Почему это происходит, разве они не должны выполняться, используя только половину ресурсов из-за GIL? Я думаю, что они используют некоторые системные вызовы и поэтому выполняются параллельно, но я не могу понять, какая часть кода выполняется на ядре системы.

вот разобрано:

dis.dis(main)
Disassembly of test:
2         0 SETUP_LOOP              16 (to 19)
    >>    3 LOAD_GLOBAL              0 (True)
          6 POP_JUMP_IF_FALSE       18

3         9 LOAD_CONST               1 (4)
         12 STORE_FAST               0 (i)
         15 JUMP_ABSOLUTE            3
    >>   18 POP_BLOCK           
    >>   19 LOAD_CONST               0 (None)
         22 RETURN_VALUE        

Также следует отметить, что когда я запускаю этот код, я не могу остановить программу, набрав Ctr+C.

Я думаю, это произошло потому, что когда у меня есть 2 ядра и 3 потока (1 основной и 2 потока), после ввода Ctr+C интерпретатор выполняет проверку после каждого тика, и хотя потоки ThreadPool привязаны к процессору и имеют низкий приоритет, основной поток не может получить GIL потому что на другом ядре поток ThreadPool ловит GIL быстрее

Но в моем понимании, где-то основной поток должен получить GIL и остановить программу, а этого не произошло за 5 минут.

Что происходит не так?


person Igor    schedule 23.05.2016    source источник
comment
поскольку GIL работает с вводом-выводом, вставьте print() / файл / сетевой ввод-вывод. p.s. stackoverflow.com/questions/29270818/   -  person strangeqargo    schedule 23.05.2016
comment
Невозможно воспроизвести. Скрипт использует только одно ядро ​​как на linux-py2.7.6, так и на win7-py2.7.11. Ctrl-C работает у меня и в обеих системах.   -  person robyschek    schedule 23.05.2016
comment
@strangeqargo, но я не вижу здесь никаких операций ввода-вывода и т. д. Что ты имеешь в виду?   -  person Igor    schedule 23.05.2016
comment
@robyschek, это очень интересно. Я использую linux Python2.7.6 futures 3.0.5 Сначала попробуйте выйти из интерпретатора Python, набрав Ctr+D, вы можете это сделать? Какую версию фьючерсов вы используете? Сколько у вас ядер? Если у вас более 2-х ядер, отключите все кроме 2-х и повторите попытку? В ожидании ответа от вас   -  person Igor    schedule 23.05.2016
comment
@Igor Я имею в виду, что он выполняется параллельно без введения GIL, потому что у него нет проблем, связанных с вводом/выводом.   -  person strangeqargo    schedule 23.05.2016
comment
@strangeqargo, я думаю, вы что-то упустили: GIL требуется для выполнения кода Python, и когда задача выполняет ввод-вывод, GIL освобождается.   -  person Igor    schedule 23.05.2016
comment
Дело не в CPython (ссылка выше про Cpython)   -  person Igor    schedule 23.05.2016
comment
Я проверил это на python 3.5.1 linux, и он использует ровно 100% процессора (1 ядро). И это прерываемо. @robyschek, можешь опубликовать трассировку стека после Ctr+C?   -  person Igor    schedule 23.05.2016
comment
Я запустил скрипт в консоли следующим образом: python the_script.py. Теперь я попытался запустить его строки в интерактивном режиме со следующим результатом: Загрузка процессора: 1 ядро, Ctrl-C, Ctrl-D - не работает. Я использую ту же версию futures, я запускаю linux на 2 ядрах, и процесс python показывает загрузку процессора около 60%.   -  person robyschek    schedule 23.05.2016
comment
@ Игорь, ты знаешь, что реализация python 2.7 по умолчанию — это cpython? Если вы используете другую версию (PyPy или IronPython), вы должны упомянуть об этом. Если вы используете системный python по умолчанию/загрузили его с веб-сайта python, вы используете cpython. Также см. этот stackoverflow.com/a/37373471/5006740 о загрузке процессора.   -  person strangeqargo    schedule 23.05.2016
comment
@Igor Он печатает две трассировки стека   -  person robyschek    schedule 23.05.2016
comment
@robyschek, спасибо за следы. Я вас правильно понял: если вы запускаете как python the_script.py, то вы можете остановить выполнение по Ctr+C. Но если вы работаете в интерактивном режиме, вы не можете выйти из интерпретатора, набрав Ctr+D? 60% процессора вы имеете в виду 60% одного ядра или 120%?   -  person Igor    schedule 23.05.2016
comment
@strangeqargo, спасибо. Я использую питон по умолчанию. может я что-то упускаю. Я прочитаю об этом.   -  person Igor    schedule 23.05.2016
comment
@Игорь, да, именно так. Когда я сказал 60% процессора, я имел в виду 60% двух ядер. Команда top показывает мне, что 120% 100% ест python, ограниченный GIL, и 20%, я думаю, переключение задач. В отсутствие GIL это было бы 200%. Py3 показывает еще меньше - 100%.   -  person robyschek    schedule 23.05.2016
comment
Ладно, я понял. Я не думаю, что эти 20% ЦП связаны с переключением задач, потому что на python3 я получаю ровно 100% максимальной загрузки (только одно ядро), а переключение задач выполняется, когда потоки Python спят.   -  person Igor    schedule 23.05.2016
comment
@Igor, я имел в виду переключение контекста, и оно легко может съесть 20%, поскольку по умолчанию пытается переключаться каждые 100 тиков (тик примерно соответствует инструкциям виртуальной машины Python). Когда я вставляю sys.setcheckinterval(1000000) в скрипт, загрузка процессора падает со 120% до 100%.   -  person robyschek    schedule 24.05.2016
comment
@robyschek, спасибо, похоже вы правы   -  person Igor    schedule 25.05.2016