Какие основные примитивы синхронизации потоков существуют в Python

Ответ

В Python для синхронизации потоков в модуле threading существуют следующие основные примитивы:

  1. Lock (мьютекс) — базовый механизм блокировки. Гарантирует, что только один поток может исполнять критическую секцию кода в один момент времени. Попытка захватить заблокированный Lock остановит поток до его освобождения.

    import threading
    
    lock = threading.Lock()
    
    with lock:
        # Этот код выполнит только один поток одновременно
        print("Критическая секция")
  2. RLock (реентерабельная блокировка) — позволяет одному и тому же потоку захватывать блокировку несколько раз без возникновения взаимной блокировки (deadlock). Поток должен освободить RLock столько же раз, сколько он его захватил. Полезна в рекурсивных функциях.

  3. Semaphore (семафор) — ограничивает количество потоков, которые могут одновременно получить доступ к общему ресурсу. Работает как счетчик: каждый вызов acquire() уменьшает его, а release() — увеличивает.

    # Не более 3 потоков могут выполнять этот блок одновременно
    semaphore = threading.Semaphore(3)
    
    with semaphore:
        # Работа с ограниченным ресурсом
        pass
  4. Event (событие) — простой механизм для коммуникации между потоками. Один поток может сигнализировать о событии (event.set()), а другие могут его ожидать (event.wait()).

  5. Condition (условие) — более сложный механизм, который объединяет Lock с возможностью ожидания определенного условия. Потоки могут ждать (condition.wait()), пока другой поток не уведомит их (condition.notify()) об изменении состояния.

  6. Barrier (барьер) — синхронизирует выполнение группы потоков, заставляя их всех ждать в определенной точке, пока не соберутся все участники.

Для синхронизации между процессами используются аналогичные примитивы из модуля multiprocessing.

Ответ 18+ 🔞

Да ты посмотри, какие у нас в Питоне инструменты для синхронизации потоков, блядь! В модуле threading целый арсенал, просто овердохуища примитивов. Сейчас разберём, кто за что отвечает, а то потом в коде бардак будет — пиздец.

1. Lock (он же мьютекс, ёпта) Это как самый простой замок на двери от сортира. Только один поток может зайти в критическую секцию, остальные ждут снаружи, блядь, пока первый не выйдет и не щёлкнет замком. Попробуй войти, когда занято — зависнешь, как идиот.

import threading

lock = threading.Lock()

with lock:
    # Этот код выполнит только один поток одновременно
    print("Критическая секция")

2. RLock (реентерабельный замок) А это уже замок поумнее. Он позволяет одному и тому же потоку заходить в одну и ту же дверь несколько раз. Удобно, если функция вызывает сама себя (рекурсия, блядь) или дергает другие функции под тем же замком. Главное — выйти ровно столько же раз, сколько зашёл, а то сам себя заблокируешь, мудак.

3. Semaphore (семафор, сука) Представь себе турникет в метро, который пропускает только, скажем, троих за раз. Вот семафор — он ровно это и делает. Счётчик есть, каждый поток, проходя, его уменьшает, а выходя — увеличивает. Если мест нет — жди, пока кто-то выйдет.

# Не более 3 потоков могут выполнять этот блок одновременно
semaphore = threading.Semaphore(3)

with semaphore:
    # Работа с ограниченным ресурсом
    pass

4. Event (событие, ёбта) Простейший способ потокам перемигиваться. Один поток кричит: «Событие случилось!» (event.set()), а другие, которые сидели и тупили в event.wait(), вдруг просыпаются и начинают суетиться. Как будильник для спящих потоков, блядь.

5. Condition (условие, хитрая жопа) Это уже не просто замок, а замок с интеллектом. Поток может не только захватить его, но и сказать: «Я буду ждать, пока не случится чё-то конкретное» (condition.wait()). Другой поток, сделав это «чё-то», может пихнуть первого (condition.notify()): «Эй, проснись, условие выполнилось!». Часто используется в паттернах типа «Производитель-Потребитель», там, где надо ждать появления данных.

6. Barrier (барьер, ёперный театр) Собрал толпу потоков и заставил их всех ждать у одной черты, пока последний, самый тормозной, не подтянется. Как только все собрались — «Старт!», и они все одновременно рвут с места. Синхронный старт, блядь, для гонок.

А вот если ты работаешь не с потоками, а с процессами (это когда multiprocessing), то там, сука, всё примерно так же, только примитивы из другого модуля берутся — multiprocessing.Lock, multiprocessing.Event и так далее. Потому что память у процессов общая не через жопу, а через специальные механизмы, вот и примитивы там свои, под это дело заточенные.