Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад
Вопрос про Python: multiprocessing, IO-bound, GIL, asyncio, threading

Почему multiprocessing не подходит для IO-bound задач?

Вопрос проверяет понимание различий между CPU-bound и IO-bound задачами и умение выбирать подходящий подход к параллелизму в Python.

Короткий ответ

Multiprocessing создаёт отдельные процессы, каждый со своей памятью и интерпретатором Python. Это хорошо для CPU-bound задач, где нужно использовать несколько ядер. Однако для IO-bound задач (например, ожидание ответа от сети или диска) создание процессов — это избыточные накладные расходы. Гораздо эффективнее использовать потоки (threading) или асинхронное программирование (asyncio), которые могут ждать в одном процессе, не блокируя друг друга, и переключаться между задачами при простое.

Длинный ответ

В Python для параллельного выполнения кода доступны несколько подходов: многопроцессность (multiprocessing), многопоточность (threading) и асинхронное программирование (asyncio). Выбор между ними зависит от природы задачи — является ли она ограниченной процессором (CPU-bound) или вводом-выводом (IO-bound).

Что такое IO-bound задачи?

IO-bound задачи — это операции, время выполнения которых в основном тратится на ожидание внешних ресурсов. К ним относятся:

  • Сетевые запросы (HTTP, API, базы данных).
  • Чтение и запись файлов на диск.
  • Ожидание пользовательского ввода.

Во время такого ожидания процессор простаивает, и его можно использовать для выполнения других задач.

Почему multiprocessing неэффективен для IO?

Модуль multiprocessing создаёт отдельные процессы операционной системы. У каждого процесса:

  • Своя независимая память (что требует копирования данных между процессами).
  • Свой интерпретатор Python и Global Interpreter Lock (GIL).
  • Значительные накладные расходы на создание и уничтожение.

Для IO-bound задач создание полноценного процесса — это "стрельба из пушки по воробьям". Пока один процесс ждёт ответа от сети, он всё равно занимает память и ресурсы ОС, а переключение между процессами (контекстный переключатель) тяжелее, чем между потоками.

Альтернативы: threading и asyncio

Для IO-bound задач в Python используют:

  • Многопоточность (threading): Потоки разделяют память одного процесса, что делает их легковеснее. Хотя GIL мешает потокам выполнять Python-код одновременно, во время операций ввода-вывода (где большая часть работы выполняется на уровне ОС) GIL отпускается, позволяя другим потокам работать.
  • Асинхронное программирование (asyncio): Использует корутины и цикл событий (event loop). Пока одна корутина ждёт IO, event loop переключается на выполнение другой. Это самый эффективный подход для высоконагруженных сетевых приложений, так как полностью избегает затрат на переключение потоков.

Пример кода: сравнение подходов

Рассмотрим задачу выполнения нескольких HTTP-запросов.

import time
import requests
from multiprocessing import Pool
from threading import Thread
import asyncio
import aiohttp

# Синхронный (последовательный) подход
def sync_fetch(urls):
    for url in urls:
        response = requests.get(url)
        print(f"Fetched {len(response.content)} bytes")

# Многопроцессный подход (multiprocessing)
def mp_fetch(urls):
    with Pool(processes=4) as pool:
        pool.map(requests.get, urls)

# Многопоточный подход (threading)
def thread_fetch(urls):
    threads = []
    for url in urls:
        t = Thread(target=requests.get, args=(url,))
        t.start()
        threads.append(t)
    for t in threads:
        t.join()

# Асинхронный подход (asyncio)
async def async_fetch(urls):
    async with aiohttp.ClientSession() as session:
        tasks = []
        for url in urls:
            task = asyncio.create_task(session.get(url))
            tasks.append(task)
        responses = await asyncio.gather(*tasks)
        for resp in responses:
            print(f"Fetched {resp.content_length} bytes")

# Запуск асинхронной функции
# asyncio.run(async_fetch(urls))

В этом примере multiprocessing будет самым медленным и ресурсоёмким для сетевых запросов из-за накладных расходов на процессы.

Вывод: Multiprocessing стоит применять для тяжёлых вычислений (CPU-bound), таких как обработка изображений или сложные математические расчёты, где нужно задействовать несколько ядер CPU. Для задач, связанных с ожиданием (сеть, диск, пользователь), всегда предпочтительнее использовать многопоточность или, что ещё лучше, асинхронное программирование.

Уровень

  • Рейтинг:

    3

  • Сложность:

    5

Навыки

  • Python

    Python

  • Node.js

    Node.js

Ключевые слова

#multiprocessing

#IO-bound

#GIL

#asyncio

#threading

Подпишись на Python Developer в телеграм