Где исполняются потоки в Python?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Где исполняются потоки в Python?
Потоки в Python исполняются в едином процессе, но управление их выполнением зависит от типа потока и механизма планирования. Разберёмся подробно.
1. Потоки ОС (OS Threads) в Python
Потоки Python — это настоящие потоки операционной системы, которые исполняются на уровне ОС, но контролируются глобальной блокировкой GIL (Global Interpreter Lock):
import threading
import time
def worker(name):
print(f"Поток {name} начал исполнение")
time.sleep(1)
print(f"Поток {name} завершился")
# Создаём и запускаем потоки
thread1 = threading.Thread(target=worker, args=("A",))
thread2 = threading.Thread(target=worker, args=("B",))
thread1.start() # Поток запускается на уровне ОС
thread2.start()
thread1.join() # Ожидаем завершения
thread2.join()
Потоки выполняются в одном процессе, но на разных ядрах ЦПУ (если ОС позволяет), однако только один может выполнять Python-код одновременно.
2. Global Interpreter Lock (GIL)
Вот ключевый момент: потоки исполняются на уровне ОС, но GIL ограничивает одновременное выполнение байт-кода Python:
import threading
import time
def cpu_bound():
"""Вычислительно интенсивная операция."""
total = 0
for i in range(100_000_000):
total += i
return total
# С потоками (GIL)
start = time.time()
thread1 = threading.Thread(target=cpu_bound)
thread2 = threading.Thread(target=cpu_bound)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"С потоками: {time.time() - start:.2f} сек") # ~ 6-7 сек
# С обычным выполнением
start = time.time()
cpu_bound()
cpu_bound()
print(f"Последовательно: {time.time() - start:.2f} сек") # ~ 3-4 сек
3. Где физически исполняются потоки
На уровне операционной системы:
- Linux: потоки исполняются как отдельные задачи в одном процессе, видны в
/proc/{pid}/task/ - Windows: потоки запускаются через API
CreateThread() - macOS: используется kernel threads через Mach API
# На Linux можно увидеть потоки
ps -eLf | grep python
# или
ls /proc/{pid}/task/ # Каждый tid — отдельный поток
4. Планирование потоков
Операционная система планирует потоки (scheduling), а Python интерпретатор контролирует доступ к GIL:
import threading
import sys
print(f"GIL включена: {hasattr(sys, getswitchinterval)}")
print(f"Интервал переключения: {sys.getswitchinterval():.4f} сек")
# Можно менять интервал переключения между потоками
sys.setswitchinterval(0.001) # Переключаться каждые 1ms
5. GIL и многопоточность
GIL позволяет потокам параллельно работать с блокирующими операциями (I/O), но не с CPU-bound кодом:
import threading
import requests
import time
def fetch_url(url):
"""I/O операция — GIL освобождается."""
response = requests.get(url)
return len(response.content)
def cpu_bound():
"""CPU операция — GIL не освобождается."""
return sum(i for i in range(10_000_000))
# Для I/O — потоки эффективны
start = time.time()
urls = ["https://example.com"] * 10
threads = [threading.Thread(target=fetch_url, args=(url,)) for url in urls]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"I/O с потоками: {time.time() - start:.2f} сек") # ~1-2 сек (параллельно)
# Для CPU — потоки неэффективны
start = time.time()
thread1 = threading.Thread(target=cpu_bound)
thread2 = threading.Thread(target=cpu_bound)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"CPU с потоками: {time.time() - start:.2f} сек") # ~4-5 сек (медленнее!)
6. Альтернативы потокам
Для CPU-bound задач:
from multiprocessing import Process
def cpu_bound():
return sum(i for i in range(100_000_000))
# Используем разные процессы (нет GIL)
p1 = Process(target=cpu_bound)
p2 = Process(target=cpu_bound)
p1.start()
p2.start()
p1.join()
p2.join()
Для I/O-bound задач:
import asyncio
async def fetch_url(url):
"""Асинхронная операция — очень эффективна."""
# await fetch(url)
pass
async def main():
await asyncio.gather(
fetch_url("url1"),
fetch_url("url2"),
)
asyncio.run(main())
7. Практический вывод
Потоки исполняются:
- На уровне ОС (разные ядра, если возможно)
- В одном процессе Python
- Под контролем GIL для CPU-bound операций
- Параллельно для I/O-bound операций
Используй потоки для: I/O операций (сеть, файлы)
Используй процессы для: CPU-интенсивных вычислений
Используй asyncio для: множества I/O операций с минимальной памятью