Python Gil странное поведение
Посмотрите на этот кусок кода:
from threading import Thread
import time
cpt = 0
def myfunction():
print("myfunction.start")
global cpt
for x in range(10):
cpt += 1
time.sleep(0.2)
print("cpt=%d" % (cpt))
print("myfunction.end")
thread1 = Thread(target=myfunction)
thread2 = Thread(target=myfunction)
thread1.start()
thread2.start()
Это очень простая функция, которая читает / записывает глобальную переменную. Я запускаю 2 темы на эту же функцию.
Я читал, что Python не очень эффективен с многопоточностью из-за GIL, который автоматически блокирует функции или методы, которые обращаются к тем же ресурсам. Итак, я думал, что Python сначала запустит thread1, а затем thread2, но я вижу в выводе консоли, что 2 потока работают параллельно. Так что я не понимаю, что Гил на самом деле блокирует...
Спасибо
1 ответ
Это из-за sleep
системный вызов, который освобождает процессор (и даже "выходит" из интерпретатора на некоторое время)
когда вы делаете time.sleep(0.2)
текущий поток приостанавливается системой (а не Python) на определенный промежуток времени, а другой поток может работать.
Обратите внимание, что print
заявления или threading.current_thread()
то, что вы можете вставить, чтобы шпионить за потоками, также (кратко) уступает системе, так что потоки могут переключаться из-за этого (вспомните кота Шредингера). Настоящий тест будет таким:
from threading import Thread
import time
cpt = 0
def myfunction():
global cpt
for x in range(10):
cpt += 1
time.sleep(0.2)
print(cpt)
thread1 = Thread(target=myfunction)
thread2 = Thread(target=myfunction)
thread1.start()
thread2.start()
Здесь вы получаете
20
20
это означает, что каждый поток работал, чтобы увеличить счетчик по очереди.
Теперь прокомментируйте time.sleep()
заявление, и вы получите:
10
20
это означает, что первый поток взял на себя все увеличивающиеся, закончил и позволил второму потоку увеличить еще 10 отсчетов. Нет системных вызовов (даже print
) убедитесь, что GIL работает в полном объеме.
GIL не вызывает проблем с производительностью, он просто предотвращает параллельную работу 2 потоков. Если вам нужно действительно запустить код Python параллельно, вы должны использовать multiprocessing
модуль вместо (со всеми его ограничениями, травление, разветвление...)