Потоки и GIL
Разберитесь с ограничениями многопоточности в Python
«Потоки и GIL» — бесплатный урок Python Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Python Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Python Academy содержит 4 уроков всего.
Что такое поток
Поток — это отдельная последовательность выполнения внутри одного процесса. Модуль Python threading позволяет выполнять функции одновременно в рамках одной программы.
import threading
def worker():
print('hello from thread')
t = threading.Thread(target=worker)
t.start()
t.join()
print('main done')Запуск нескольких потоков
Можно запустить несколько потоков. Каждый выполняет свою целевую функцию. Вызов join() заставляет основной поток ждать завершения другого потока.
import threading
def task(name):
print('task', name)
threads = [threading.Thread(target=task, args=(i,)) for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
print('all finished')Глобальная блокировка интерпретатора
В CPython есть глобальная блокировка интерпретатора (GIL): в каждый момент байткод Python выполняет только один поток. Поэтому потоки не обеспечивают настоящего ускорения за счёт параллельного выполнения чистого кода Python, связанного с вычислениями.
import sys
print('Running on:', sys.implementation.name)
print('Only one thread runs Python bytecode at a time due to the GIL')Когда потоки всё же полезны
Потоки особенно эффективны для задач, связанных с вводом-выводом: сетевых запросов, чтения с диска и ожидания. Пока один поток ждёт, GIL освобождается, и может работать другой поток.
import threading, time
def fake_io(name):
time.sleep(0.1)
print('done', name)
start = time.perf_counter()
ts = [threading.Thread(target=fake_io, args=(i,)) for i in range(3)]
for t in ts: t.start()
for t in ts: t.join()
print('elapsed about 0.1s, not 0.3s')Вычислительные задачи не ускоряются
Для тяжёлых вычислений добавление потоков не ускорит выполнение, поскольку GIL сериализует их работу. Для вычислительных задач используйте multiprocessing.
import threading
def burn():
total = 0
for i in range(1000000):
total += i
return total
t = threading.Thread(target=burn)
t.start()
t.join()
print('CPU-bound: threads share one core under the GIL')Потоки-демоны
Поток-демон автоматически завершается, когда завершается основная программа. Используйте его для фоновых вспомогательных задач, завершения которых не требуется ждать.
import threading, time
def background():
time.sleep(0.05)
print('background tick')
t = threading.Thread(target=background, daemon=True)
t.start()
t.join()
print('main exits')Именование и идентификация потоков
У каждого потока есть имя и идентификатор. threading.current_thread() возвращает объект выполняющегося потока, что удобно для журналирования.
import threading
def show():
t = threading.current_thread()
print('running in', t.name)
t = threading.Thread(target=show, name='Worker-1')
t.start()
t.join()
print('main thread:', threading.current_thread().name)Наследование от Thread
Вместо передачи целевой функции можно создать подкласс Thread и переопределить run(). Это полезно, когда потоку требуется собственное состояние.
import threading
class Greeter(threading.Thread):
def __init__(self, name):
super().__init__()
self.who = name
def run(self):
print('hi', self.who)
g = Greeter('Sam')
g.start()
g.join()Возврат результатов из потоков
Возвращаемое значение целевой функции отбрасывается. Чтобы собрать результаты, запишите их в общую структуру данных; позднее Вы познакомитесь с более безопасными инструментами, например очередями.
import threading
results = {}
def compute(key):
results[key] = key * key
ts = [threading.Thread(target=compute, args=(i,)) for i in range(4)]
for t in ts: t.start()
for t in ts: t.join()
print(results)Подсчёт активных потоков
threading.active_count() сообщает, сколько потоков сейчас работает, включая основной поток.
import threading, time
def wait():
time.sleep(0.05)
ts = [threading.Thread(target=wait) for _ in range(2)]
for t in ts: t.start()
print('active now:', threading.active_count())
for t in ts: t.join()
print('active after join:', threading.active_count())Выбор между потоками и процессами
Общее правило: используйте потоки для параллельного выполнения задач, связанных с вводом-выводом, а процессы — для параллельных вычислений. Причина — GIL.
tasks = {'download files': 'threads', 'crunch numbers': 'processes', 'call APIs': 'threads'}
for task, choice in tasks.items():
print(task, '->', choice)Быстрая проверка
Проверьте, насколько хорошо Вы понимаете потоки и GIL.
Итоги
Вы узнали о потоках и GIL:
- Потоки выполняются одновременно внутри одного процесса с помощью
threading.Thread. - GIL сериализует выполнение байткода Python.
- Потоки полезны для задач, связанных с вводом-выводом, а процессы — для вычислительных задач.
- Используйте
start(),join(), потоки-демоны и наследование от классов.
Далее Вы узнаете, как удобно управлять потоками с помощью ThreadPoolExecutor.
Часто задаваемые вопросы
Урок «Потоки и GIL» бесплатный?
Да — полный текст урока «Потоки и GIL» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Python Academy, подпишись на CoddyKit PRO. Курс Python Academy содержит 4 уроков всего.
Чему я научусь в уроке «Потоки и GIL»?
Разберитесь с ограничениями многопоточности в Python Ты практикуешь Python Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Python Academy?
Предыдущий опыт не требуется. Python Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Потоки и GIL»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Python Academy?
Да. Каждый урок Python Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.