Ответ
Основная проблема — состояние гонки (race condition). Она возникает, когда результат выполнения кода зависит от непредсказуемой последовательности операций в нескольких потоках, одновременно обращающихся к общим данным.
Чаще всего это происходит из-за неатомарных операций. Например, операция инкремента (counter += 1) на самом деле состоит из трех шагов:
- Чтение текущего значения переменной.
- Увеличение значения на 1.
- Запись нового значения обратно в переменную.
Если два потока выполняют эту операцию одновременно, один из них может перезаписать результат другого, что приведет к потере данных.
Пример на Python, демонстрирующий race condition:
import threading
# Общая глобальная переменная
counter = 0
def increment():
global counter
for _ in range(100000):
# Критическая секция: неатомарная операция
counter += 1
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
# Ожидаемый результат: 1_000_000
# Реальный результат будет меньше из-за потерянных инкрементов
print(f"Финальное значение счетчика: {counter}")
Другие связанные проблемы:
- Deadlock (взаимная блокировка): Два или более потока вечно ожидают друг друга, чтобы освободить ресурсы, которые они заблокировали.
- Неконсистентность данных: Чтение данных в промежуточном, невалидном состоянии, пока другой поток их изменяет.
Решение:
Для предотвращения состояния гонки используются механизмы синхронизации, такие как мьютексы (locks). Они гарантируют, что только один поток может одновременно выполнять код в критической секции.
Исправленный пример с threading.Lock:
import threading
counter = 0
lock = threading.Lock()
def safe_increment():
global counter
for _ in range(100000):
with lock: # Блокировка на время выполнения критической секции
counter += 1
# ... (создание и запуск потоков с target=safe_increment)
# ...
# print(counter) -> Выведет корректный результат 1_000_000