Метод печати и GIL

У меня есть многопоточная программа, и недавно я столкнулся с интересным явлением.

Если я вызову метод print в рабочем потоке, программа станет очень реактивной. В этом нет ничего сложного, просто вызов метода print решает все проблемы.

Недавно я прочитал статью о глобальной блокировке интерпретатора Python, также известной как GIL, и в ней говорилось, что GIL выпускается после выполнения связанного с вводом-выводом материала. Как вы думаете, вызов print также связан с вводом-выводом?

Я действительно хотел бы сделать мою программу реактивной, но явно неудобно выгружать данные на стандартный вывод во время ее работы. Поэтому я попытался перенаправить вывод на /dev/null, но это не решило проблему:

with contextlib.redirect_stdout(None):
    print('')

Буду признателен, если у вас есть идея, чтобы я мог воспроизвести тот же эффект с помощью следующего вызова, но ничего не сбрасывая:

print('')

Насколько я вижу явление, GIL выпускается, пока интерпретатор работает на print(''). Может быть, мне нужен такой короткий перерыв, который освободит меня от GIL.

Это только для вашей информации, но я попытался вызвать следующий метод:

print('', end='', flush=True)

Конечно, он ничего не выгрузил, но моя программа стала немного неровной, и казалось, что поток занял время выполнения, поэтому другие потоки запускались очень редко.

Обновить

Если я вызову usleep(1) из QThread, ожидая, что он заснет на 1 мкс, то он будет ждать намного дольше, чем я указал. Например. рабочий поток запускается каждую 1 мс, что очень медленно, потому что я ожидал запускать его в микросекундном порядке. Вызов print('') запускает поток через несколько микросекунд. В этом смысле я называю его реактивным.

Обновить

Я чувствую, что что-то замедляет время выполнения потока, но это не usleep или time.sleep(). Однако я столкнулся с тем, что print может выкинуть блокировщик. Поэтому я хотел бы знать, что на самом деле отбрасывает блокировщик.


person Doofah    schedule 14.09.2018    source источник
comment
print — относительно медленная функция, поэтому потенциально она может изменить время работы функций, которые ее используют. Будьте осторожны, так как вы можете просто удачно скрыть состояние гонки из-за добавления функции print.   -  person all or None    schedule 14.09.2018
comment
Спасибо за ваш комментарий! Я понимаю ваш совет, но я не придерживаюсь метода печати. Мне просто нужен способ, который делает поток быстрым. Первоначально я пытался вызвать uleep(1) класса PyQt QThread, но, похоже, он спал намного дольше, чем я указал. Поэтому я удалил usleep и понял, что поток становится очень реактивным, просто вызывая print('').   -  person Doofah    schedule 14.09.2018
comment
Что вы имеете в виду под реактивным?   -  person all or None    schedule 14.09.2018
comment
Если я вызову usleep(1) из QThread, ожидая, что он заснет на 1 мкс, то он будет ждать намного дольше, чем я указал. Например. рабочий поток запускается каждую 1 мс, что очень медленно, потому что я ожидал запускать его в микросекундном порядке. Вызов print('') запускает поток через несколько микросекунд. В этом смысле я называю его реактивным.   -  person Doofah    schedule 14.09.2018
comment
Я чувствую, что что-то замедляет время выполнения потока, но это не usleep или time.sleep(). Однако я столкнулся с тем, что print может выкинуть блокировщик. Поэтому я хотел бы знать, что на самом деле отбрасывает блокировщик.   -  person Doofah    schedule 14.09.2018
comment
Попробуйте time.sleep(0); это должно просто дать GIL, а затем немедленно встать в очередь, чтобы получить его снова. Тем не менее, если вы работаете в Windows, функции WinAPI для блокировки ожиданий (на основе которых построены GIL и все другие блокирующие ожидания по времени) не имеют очень хорошей детализации; если он вообще спит, а другой поток не выпускает GIL самостоятельно, скорее всего, для возврата потребуется несколько миллисекунд.   -  person ShadowRanger    schedule 14.09.2018


Ответы (1)


Итак, здесь происходят две вещи. Во-первых, для самого GIL большинство функций ввода-вывода освобождают его непосредственно перед вызовом кода платформы, поэтому вызов print определенно освобождает его. Это, естественно, позволит среде выполнения запланировать другой поток.

Во-вторых, для usleep эта функция гарантированно будет спать минимум столько микросекунд, сколько вы просите, но почти не будет спать меньше, чем продолжительность ОС. галочка планировщика. В Linux это часто работает на частоте 1000 Гц, 250 Гц или 100 Гц, но может немного отличаться.

Теперь, если вам нужно что-то более детальное, есть вызов nanosleep, который " busy-wait" для задержек менее 2 мс вместо обращения к ядру.

person Community    schedule 14.09.2018
comment
Спасибо за подсказку! Я думаю, что я могу попытаться получить информацию. Я приму ваш ответ, как только подтвержу результат. Еще раз спасибо! - person Doofah; 14.09.2018