Какие основные инструменты для многопоточности используются в Python?

Ответ

В Python для реализации многопоточности, в основном для I/O-bound задач, используются инструменты из стандартной библиотеки. Главным ограничением является Global Interpreter Lock (GIL), который не позволяет нескольким потокам одновременно выполнять Python байт-код на многоядерных CPU.

1. Модуль threading

Это низкоуровневый модуль для прямого управления потоками.

  • threading.Thread: Основной класс для создания потока.
  • Примитивы синхронизации: Lock, RLock, Semaphore, Event для предотвращения состояний гонки (race conditions) при доступе к общим данным.

Пример:

import threading
import time

def worker(name):
    print(f"Поток {name} начал работу")
    time.sleep(1)
    print(f"Поток {name} завершил работу")

threads = []
for i in range(3):
    # Создаем и запускаем поток
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()

# Ожидаем завершения всех потоков
for t in threads:
    t.join()

print("Все потоки завершены")

2. Модуль concurrent.futures

Это высокоуровневый интерфейс для асинхронного выполнения задач, который упрощает работу с пулами потоков.

  • ThreadPoolExecutor: Управляет пулом потоков, автоматически распределяя задачи.

Пример:

import concurrent.futures
import time

def worker(name):
    time.sleep(1)
    return f"Результат от потока {name}"

# Создаем пул из 3 потоков
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    # Отправляем задачи на выполнение
    futures = [executor.submit(worker, i) for i in range(3)]

    # Получаем результаты по мере готовности
    for future in concurrent.futures.as_completed(futures):
        print(future.result())

Вывод:

  • Используйте threading для полного контроля над жизненным циклом потоков и сложной синхронизации.
  • Используйте concurrent.futures.ThreadPoolExecutor для более простых задач, где нужно просто выполнить набор функций в отдельных потоках и получить результат.

Ответ 18+ 🔞

А, ну ты про потоки в питоне, да? Ну это, блядь, классика жанра, как Герасим с Муму, только с кодом. Слушай сюда, сейчас разжую.

Так вот, в питоне, если тебе надо, чтобы несколько дел одновременно делалось — типа файлы качать или запросы в сеть слать — тебе на потоки. Но тут есть одна, сука, засада — Global Interpreter Lock (GIL). Эта, блядь, штука, как ревнивая баба, не даёт нескольким потокам одновременно Python-код исполнять на всех ядрах процессора. То есть, по сути, они друг другу мешают, очередь выстраивают. Для вычислений, где мозги нужны (CPU-bound), это пиздец как неэффективно, а вот для дел, где ты в основном ждёшь (I/O-bound) — самое то.

1. Модуль threading — это как вручную всё делать

Низкоуровневый такой, для тех, кто любит поковыряться. Тут тебе и потоки создавать, и замки на них вешать, чтобы они друг у друга из-под носа данные не воровали.

Смотри, как это выглядит:

import threading
import time

def worker(name):
    print(f"Поток {name} начал работу")
    time.sleep(1)  # Притворяется, что работает, а сам спит, ленивая жопа
    print(f"Поток {name} завершил работу")

threads = []
for i in range(3):
    # Рождаем поток, как Герасим Муму нашёл
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()  # И пускаем в свободное плавание

# А теперь ждём, пока все эти твари закончат
for t in threads:
    t.join()  # Стоим, как дураки, и ждём

print("Все потоки завершены")

Вот тут ты сам за всё отвечаешь: запустил, подождал, замок на дверь повесил, если надо. Сам себе режиссёр, оператор и Герасим.

2. Модуль concurrent.futures — для ленивых и умных

Это уже высший пилотаж, ёпта. Тебе не надо возиться с каждой ниткой по отдельности. Ты создаёшь пул — типа бригаду грузчиков — и кидаешь им задачи. Они сами разберутся.

Вот, смотри, как элегантно:

import concurrent.futures
import time

def worker(name):
    time.sleep(1)
    return f"Результат от потока {name}"  # Вернул и молодец

# Создаём пул из трёх работяг
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    # Кидаем им три задания, как кости собаке
    futures = [executor.submit(worker, i) for i in range(3)]

    # А теперь собираем урожай, что созрело
    for future in concurrent.futures.as_completed(futures):
        print(future.result())  # Достаём результат, как из кармана

*Итог, чтобы не ебть себе мозг:**

  • threading — бери, если тебе надо, блядь, полный контроль, сложные танцы с замками и событиями. Сам всё делаешь, сам и отвечаешь.
  • concurrent.futures.ThreadPoolExecutor — бери, если тебе просто кучу похожих задач выполнить, а результат собрать. Красота, а не инструмент. Не надо изобретать велосипед, всё уже придумали до нас, пидарасы умные.

Вот и вся философия. Выбирай, что тебе ближе по душе, и не загоняйся. Главное — GIL не забывай, а то будешь как Герасим, который немой: хочешь сказать, а нихуя не получается.