Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Мьютекс
Мьютекс (Mutual Exclusion) — это синхронизационный примитив, который используется для обеспечения взаимного исключения при доступе к общим ресурсам в многопоточной программе. Мьютекс гарантирует, что только один поток может получить доступ к защищаемому ресурсу одновременно.
Основной принцип
Мьютекс работает как бинарный замок:
- Заблокирован (locked) — ресурс занят одним потоком
- Разблокирован (unlocked) — ресурс свободен
Когда поток попытается получить заблокированный мьютекс, он будет заблокирован (suspend) и ждать освобождения ресурса.
Использование в Python
В Python основной инструмент — threading.Lock() или threading.RLock():
import threading
lock = threading.Lock()
balance = 0
def deposit_with_lock(amount):
global balance
with lock:
temp = balance
temp += amount
balance = temp
thread1 = threading.Thread(target=deposit_with_lock, args=(100,))
thread2 = threading.Thread(target=deposit_with_lock, args=(200,))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
RLock (Re-entrant Lock)
RLock позволяет одному потоку несколько раз заблокировать один мьютекс:
import threading
lock = threading.RLock()
def recursive_function(n):
with lock:
if n > 0:
print(f"Level {n}")
recursive_function(n - 1)
recursive_function(3)
Более сложный пример: Thread-Safe Queue
import threading
class ThreadSafeQueue:
def __init__(self):
self.items = []
self.lock = threading.Lock()
def put(self, item):
with self.lock:
self.items.append(item)
def get(self):
with self.lock:
if self.items:
return self.items.pop(0)
return None
def size(self):
with self.lock:
return len(self.items)
queue = ThreadSafeQueue()
def producer():
for i in range(5):
queue.put(f"item-{i}")
print(f"Produced item-{i}")
def consumer():
for _ in range(5):
item = queue.get()
if item:
print(f"Consumed {item}")
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
t1.join()
t2.join()
Проблемы с мьютексами
Deadlock — два потока ждут друг друга:
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread_one():
with lock1:
with lock2:
print("Thread 1")
def thread_two():
with lock2:
with lock1:
print("Thread 2")
Этот код может привести к deadlock'у, если потоки захватят блокировки в противоположном порядке.
Альтернативы с asyncio
import asyncio
lock = asyncio.Lock()
async def critical_section():
async with lock:
await asyncio.sleep(1)
print("Critical section")
async def main():
await asyncio.gather(
critical_section(),
critical_section(),
)
asyncio.run(main())
Лучшие практики
- Используй context manager (with lock:) для гарантированного освобождения
- Минимизируй время внутри критической секции
- Избегай вложенных блокировок или используй RLock
- Для асинхронного кода используй asyncio.Lock вместо threading.Lock
- В production используй Higher-level инструменты (Queue, Event, Condition)
Мьютексы — основа многопоточного программирования, но их необходимо использовать осторожно, чтобы избежать deadlock'ов и других проблем с синхронизацией.