Нужен ли Python GIL, если расширение однопоточное

Я пишу расширение C++ для своего приложения Python. Я понимаю, что Python GIL используется для предотвращения одновременного доступа нескольких потоков к PyObject. Тем не менее, мои вопросы заключаются в том, что мой код расширения является однопоточным, нужно ли мне приобретать GIL?

Допустим, в приложении Python есть такие коды, как

import time
from threading import Thread

COUNT = 50000000

def countdown(n):
    while n>0:
        n -= 1

t1 = Thread(target=countdown, args=(COUNT//2,))
t2 = Thread(target=countdown, args=(COUNT//2,))

start = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end = time.time()

print('Time taken in seconds -', end - start)

Я знаю, что хотя есть 2 потока, они не будут выполняться параллельно из-за GIL. Что, если я вызову расширение C++ в обоих потоках, а расширение C++ будет однопоточным? Нужно ли мне учитывать GIL?

Или другой вопрос, я предполагаю, что поток попытается удержать GIL в коде Python. Если он покинет Python и будет выполнять код на C++, будет ли выпущен GIL?


person Howard Yu    schedule 05.08.2019    source источник
comment
Короткий ответ: вы можете выпустить GIL в своем расширении, но вам нужно быть осторожным, чтобы повторно получить его.   -  person Iain Shelvington    schedule 06.08.2019


Ответы (1)


Я предполагаю, что мы говорим о CPython и GIL.

Реализация гарантирует, что GIL удерживается, когда интерпретатор передает управление вашему C++-расширению. Это означает, что вы можете свободно обращаться к интерпретатору, проверять любой объект и т. д.; любой другой поток, которому потребуется доступ к интерпретатору, гарантированно будет заблокирован при попытке получить GIL. Это также означает, что вашему C++-расширению необходимо вообще учитывать GIL, если оно не заботится о потоках, которые обращаются к интерпретатору.

Недостаток такого подхода, ориентированного на безопасность, заключается в том, что реализация блокирует весь доступ к интерпретатору во время выполнения кода вашего расширения. Если вашему коду расширения не требуется доступ к интерпретатору - например. поскольку он может работать с локальными копиями/буферами или вот-вот заблокируется при вводе-выводе, вам следует немедленно освободить GIL, чтобы другие потоки могли работать, и повторно получить его, прежде чем вернуться к интерпретатору.

Рассмотрите возможность наличия двух потоков Python, пытающихся вызвать ваше расширение с новой работой. Первый поток будет запланирован, заблокирует интерпретатор, вызовет ваше расширение и дождется его возврата. Когда он возвращается, интерпретатор переключается на второй поток, и только тогда этот поток может вызывать ваше расширение. Теперь он должен ждать, блокируя поток 1. Фактически у вас есть параллельные потоки, которые никогда не выполняются параллельно. Освобождение GIL из расширения позволяет другому потоку продолжить работу. Это не обязательно, чтобы программа была правильной.

person user2722968    schedule 05.08.2019