Ответ
Многопоточность в Python применяется для совместного выполнения I/O-bound операций (работа с сетью, файлами, базами данных), где потоки могут работать, пока один ожидает ответа. Однако из-за GIL (Global Interpreter Lock) потоки не выполняют CPU-bound задачи параллельно — для этого используется модуль multiprocessing.
Пример для I/O-bound задач (потоки):
import threading
import time
def io_task(task_id):
print(f"Задача {task_id}: начало ожидания I/O")
time.sleep(2) # Имитация I/O-операции
print(f"Задача {task_id}: завершена")
threads = []
for i in range(3):
t = threading.Thread(target=io_task, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
print("Все I/O-задачи завершены.")
Пример для CPU-bound задач (процессы):
from multiprocessing import Process
import math
def cpu_task(n):
result = sum(math.sqrt(i) for i in range(n))
print(f"Вычисление завершено, результат: {result:.2f}")
if __name__ == '__main__':
p = Process(target=cpu_task, args=(1_000_000,))
p.start()
p.join()
Ключевые выводы:
- Потоки (
threading) — для операций, связанных с ожиданием (I/O). - Процессы (
multiprocessing) — для параллельных вычислений (CPU). - GIL ограничивает выполнение только одного потока Python одновременно, поэтому потоки не ускоряют CPU-bound код.
Ответ 18+ 🔞
Да блядь, слушай, вот тебе про многопоточность в Питоне, чтоб ты понимал, в чём тут собака зарыта. Это ж не просто так, сука, взял и накрутил потоков — нихуя не выйдет, если не знать, где их применять.
Вот смотри, если у тебя задачи I/O-bound — это когда твоя программа тупо ждёт, как лох: ответ от сервера, чтение с диска, запрос в базу данных. Вот тут потоки — самое то, блядь! Пока один поток спит, ожидая ответа, другие могут работать. Это как в очереди в поликлинике: пока один идёт на приём, остальные сидят и хуйней страдают.
Вот, держи пример, чтоб глаза на лоб не полезли:
import threading
import time
def io_task(task_id):
print(f"Задача {task_id}: начало ожидания I/O")
time.sleep(2) # Имитация I/O-операции
print(f"Задача {task_id}: завершена")
threads = []
for i in range(3):
t = threading.Thread(target=io_task, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
print("Все I/O-задачи завершены.")
Видишь? Три задачи стартанули почти одновременно, потому что они ждут, блядь. А не считают.
А теперь, внимание, ёпта! Если у тебя задачи CPU-bound — это когда процессор пашет, как папа Карло, вычисляя что-то тяжёлое (типа перемножения матриц или там поиска простых чисел) — тут потоки нихуя не помогут! Вообще! Потому что в Питоне есть такая хуйня, называется GIL (Global Interpreter Lock). Это такая сраная заглушка, которая позволяет в один момент времени работать только одному потоку с байт-кодом Питона. Представь, у тебя восемь ядер, а работает только одно, остальные семь — просто болтаются, как сиськи на бабушке. Пиздец, да?
Поэтому для CPU-задач надо использовать процессы, блядь! У каждого процесса свой интерпретатор, свой GIL, и они могут на разных ядрах реально параллельно работать. Вот, смотри, как это выглядит:
from multiprocessing import Process
import math
def cpu_task(n):
result = sum(math.sqrt(i) for i in range(n))
print(f"Вычисление завершено, результат: {result:.2f}")
if __name__ == '__main__':
p = Process(target=cpu_task, args=(1_000_000,))
p.start()
p.join()
Видишь разницу? Это уже не потоки, а отдельный процесс. Он запустится и будет пахать сам по себе, не мешая основному.
Итог, чтоб ты запомнил, как Отче наш:
- Потоки (
threading) — для операций, где много ожидания (I/O). Сеть, файлы, базы. Пока один ждёт, другие работают. - Процессы (
multiprocessing) — для тяжёлых вычислений (CPU). Каждый процесс на своём ядре, GIL не мешает. - GIL — это такая питоновская придурь, из-за которой потоки нихуя не ускоряют вычисления. Просто запомни: для счёта — процессы, для ожидания — потоки. Всё, вопрос закрыт, иди работай.