← Назад к вопросам

Что такое процесс в Python?

1.3 Junior🔥 181 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое процесс в Python

Процесс в Python (как и в любой операционной системе) — это отдельный экземпляр программы, работающий с собственным адресным пространством памяти, независимым от других процессов. Это фундаментальное понятие в многопроцессной обработке и параллелизме.

Основные характеристики процесса

1. Независимое адресное пространство памяти

Каждый процесс имеет собственную память, изолированную от других процессов:

import os
from multiprocessing import Process

global_var = 100  # Глобальная переменная в главном процессе

def change_variable():
    global global_var
    global_var = 200
    print(f"В процессе: global_var = {global_var}, PID = {os.getpid()}")

if __name__ == "__main__":
    print(f"Главный процесс: global_var = {global_var}, PID = {os.getpid()}")
    
    p = Process(target=change_variable)
    p.start()
    p.join()
    
    # Результат:
    # В процессе: global_var = 200, PID = 12345
    # Главный процесс: global_var = 100, PID = 98765
    # Изменение в дочернем процессе не повлияло на главный процесс!
    print(f"Главный процесс после: global_var = {global_var}, PID = {os.getpid()}")

2. PID (Process ID) — уникальный идентификатор

Каждому процессу операционная система выделяет уникальный номер:

import os
from multiprocessing import Process

def show_pid():
    print(f"Мой PID: {os.getpid()}")
    print(f"PID моего родителя: {os.getppid()}")

if __name__ == "__main__":
    print(f"Главный процесс PID: {os.getpid()}")
    
    for i in range(3):
        p = Process(target=show_pid, name=f"Worker-{i}")
        p.start()
        p.join()

3. Независимый GIL (Global Interpreter Lock)

Самое важное: каждый процесс имеет собственный GIL! Это означает, что процессы могут выполняться на разных ядрах процессора параллельно:

import time
from multiprocessing import Process

def cpu_heavy_task(n):
    """Процессор-интенсивная задача"""
    count = 0
    for i in range(n):
        count += i
    return count

if __name__ == "__main__":
    # Тестируем на 4 ядрах
    n = 100_000_000
    
    # Однопроцессный вариант (с GIL)
    start = time.time()
    cpu_heavy_task(n)
    cpu_heavy_task(n)
    cpu_heavy_task(n)
    cpu_heavy_task(n)
    single_time = time.time() - start
    print(f"Однопроцессно: {single_time:.2f} сек")
    
    # Многопроцессный вариант (без GIL между процессами)
    start = time.time()
    processes = []
    for _ in range(4):
        p = Process(target=cpu_heavy_task, args=(n,))
        p.start()
        processes.append(p)
    
    for p in processes:
        p.join()
    
    multi_time = time.time() - start
    print(f"Многопроцессно: {multi_time:.2f} сек")
    print(f"Ускорение: {single_time / multi_time:.2f}x")
    # Результат: примерно 3-4x ускорение на 4 ядрах!

Процесс vs Поток (Thread) vs Корутина (Coroutine)

ХарактеристикаПроцессПотокКорутина
ПамятьОтдельная для каждогоОбщая в процессеОбщая в процессе
GILСвой для каждогоОбщий (блокирует друг друга)Нет GIL, но один за раз
СозданиеМедленно (100+ ms)Быстро (1 ms)Очень быстро (микросекунды)
Переключение контекстаДорого (ОС контролирует)Дорого (ОС контролирует)Дешево (программа контролирует)
СинхронизацияСложная (IPC)Проще (shared memory)Не нужна
ПараллелизмИстинный параллелизмПсевдопараллелизм (GIL)Квазипараллелизм

Создание процессов в Python

1. multiprocessing.Process

from multiprocessing import Process
import time

def worker(name, duration):
    print(f"{name} начал работу")
    time.sleep(duration)
    print(f"{name} завершил работу")

if __name__ == "__main__":
    # Создание процессов
    p1 = Process(target=worker, args=("Worker-1", 2))
    p2 = Process(target=worker, args=("Worker-2", 3))
    
    # Запуск
    p1.start()
    p2.start()
    
    # Ожидание завершения
    p1.join()
    p2.join()
    
    print("Все процессы завершены")

2. ProcessPoolExecutor для пула процессов

from concurrent.futures import ProcessPoolExecutor
import time

def calculate_square(n):
    time.sleep(1)  # Имитация работы
    return n * n

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5]
    
    # Пул из 4 процессов
    with ProcessPoolExecutor(max_workers=4) as executor:
        results = list(executor.map(calculate_square, numbers))
    
    print(results)  # [1, 4, 9, 16, 25]
    # Время выполнения: ~1 сек (параллельно), вместо 5 сек (последовательно)

Обмен данными между процессами (IPC — Inter-Process Communication)

1. Queue — очередь

from multiprocessing import Process, Queue

def producer(q):
    for i in range(5):
        q.put(i)
    q.put(None)  # Сигнал конца

def consumer(q):
    while True:
        item = q.get()
        if item is None:
            break
        print(f"Получено: {item}")

if __name__ == "__main__":
    q = Queue()
    
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    
    p1.start()
    p2.start()
    
    p1.join()
    p2.join()

2. Pipe — двусторонний канал

from multiprocessing import Process, Pipe

def child_process(conn):
    conn.send("Привет из дочернего процесса")
    print(f"Дочерний получил: {conn.recv()}")
    conn.close()

if __name__ == "__main__":
    parent_conn, child_conn = Pipe()
    
    p = Process(target=child_process, args=(child_conn,))
    p.start()
    
    print(f"Родитель получил: {parent_conn.recv()}")
    parent_conn.send("Привет из родительского процесса")
    
    p.join()

3. Shared Memory — общая память

from multiprocessing import Process, Value, Array

def increment_value(shared_val):
    with shared_val.get_lock():
        shared_val.value += 1

def modify_array(shared_arr):
    for i in range(len(shared_arr)):
        shared_arr[i] *= 2

if __name__ == "__main__":
    # Общее целое число
    shared_int = Value('i', 0)  # 'i' = integer type
    
    # Общий массив
    shared_array = Array('i', [1, 2, 3, 4, 5])
    
    processes = []
    for _ in range(5):
        p = Process(target=increment_value, args=(shared_int,))
        processes.append(p)
        p.start()
    
    p2 = Process(target=modify_array, args=(shared_array,))
    p2.start()
    processes.append(p2)
    
    for p in processes:
        p.join()
    
    print(f"Итоговое значение: {shared_int.value}")  # 5
    print(f"Итоговый массив: {list(shared_array)}")  # [2, 4, 6, 8, 10]

Состояния процесса

from multiprocessing import Process
import time

def long_task():
    time.sleep(5)

if __name__ == "__main__":
    p = Process(target=long_task)
    
    print(f"Перед стартом: is_alive() = {p.is_alive()}")  # False
    
    p.start()
    print(f"После старта: is_alive() = {p.is_alive()}")   # True
    
    time.sleep(1)
    print(f"Процесс активен: {p.is_alive()}")             # True
    
    p.join()
    print(f"После join: is_alive() = {p.is_alive()}")     # False

Когда использовать процессы

# 1. CPU-bound задачи (обработка данных, вычисления)
from concurrent.futures import ProcessPoolExecutor

def heavy_computation(data):
    return sum(data)

if __name__ == "__main__":
    large_datasets = [[i]*1000000 for i in range(1, 5)]
    
    with ProcessPoolExecutor(max_workers=4) as executor:
        results = executor.map(heavy_computation, large_datasets)

# 2. Изоляция для безопасности
from multiprocessing import Process

def run_untrusted_code():
    # Даже если код сломается, главный процесс выживет
    eval(user_input)

if __name__ == "__main__":
    p = Process(target=run_untrusted_code)
    p.start()
    p.join()

# 3. Долгоживущие фоновые задачи
def background_worker():
    while True:
        # Бесконечный рабочий цикл
        pass

if __name__ == "__main__":
    p = Process(target=background_worker, daemon=True)
    p.start()

Важные замечания

# ОШИБКА: забыли if __name__ == "__main__"
from multiprocessing import Process

def worker():
    print("Работаю")

Process(target=worker).start()  # На Windows зависнет!

# ПРАВИЛЬНО:
if __name__ == "__main__":
    Process(target=worker).start()

# Это нужно потому что на Windows используется spawn, а на Unix fork
# На Windows нужно переимпортировать модуль, поэтому код должен быть в защищённом блоке

Заключение

Процесс в Python — это мощный инструмент для истинного параллелизма при работе с CPU-intensive задачами. Каждый процесс имеет собственное адресное пространство и GIL, что позволяет выполняться на разных ядрах процессора одновременно. Однако создание процессов дороже, чем потоков, так что используй их для тяжёлых вычислительных задач.