Ответ
Да, потоки в Python, созданные с помощью модуля threading
, работают в рамках одного процесса и используют общее адресное пространство. Это означает, что они имеют доступ к одним и тем же данным и объектам в памяти, включая глобальные переменные.
Ключевые особенности:
- Эффективность для I/O-bound задач: Потоки отлично подходят для задач, где программа ожидает внешние ресурсы (например, сетевой ответ или чтение с диска), так как один поток может выполняться, пока другой заблокирован в ожидании.
- Проблема GIL (Global Interpreter Lock): В стандартной реализации CPython GIL не позволяет нескольким потокам одновременно выполнять Python-байткод. Это делает потоки неэффективными для параллельных вычислений на многоядерных процессорах (CPU-bound задач).
- Состояние гонки (Race Condition): Из-за общего доступа к памяти одновременное изменение данных несколькими потоками может привести к непредсказуемым результатам. Для предотвращения этого необходимо использовать примитивы синхронизации.
Пример состояния гонки:
import threading
# Общий ресурс
counter = 0
def increment():
global counter
for _ in range(100000):
counter += 1
threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
# Ожидаемый результат: 500000
# Реальный результат будет меньше из-за состояния гонки
print(f"Финальное значение счетчика: {counter}")
Решение с использованием Lock
:
Для обеспечения атомарности операции инкремента используется блокировка threading.Lock
.
import threading
counter = 0
lock = threading.Lock()
def safe_increment():
global counter
for _ in range(100000):
with lock: # Блокировка захватывается перед изменением
counter += 1
# Блокировка автоматически освобождается при выходе из блока 'with'
# ... (аналогичный запуск потоков)
Использование lock
гарантирует, что только один поток может изменять counter
в любой момент времени, обеспечивая корректный результат.