0Pricing
Python Academy · Урок

Потоки и 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 — локальная установка не требуется.

Все уроки этого курса

  1. Потоки и GIL
  2. ThreadPoolExecutor
  3. Основы multiprocessing
  4. Безопасный обмен данными
← Назад к Python Academy