Процесс — единица изоляции. Операционная система выдаёт ему виртуальное адресное пространство (код, данные, куча, стеки), таблицу открытых файлов, идентификатор, права пользователя. Один процесс не может прочитать память другого — это защищает систему от чужих ошибок.
Поток — единица планирования: последовательность инструкций, которую планировщик ставит на ядро процессора. В процессе есть хотя бы один поток. Потоки одного процесса видят одну и ту же память, поэтому обмениваться данными им просто — достаточно общей переменной. Но ровно отсюда гонки данных и необходимость синхронизации.
У каждого потока своё:
- стек — локальные переменные и цепочка вызовов;
- регистры и счётчик команд;
- состояние планирования (работает, готов, ждёт).
Общее у потоков процесса — куча, статические данные, код, открытые файлы и сокеты, рабочий каталог.
Цена. Создать процесс дороже: нужно новое адресное пространство (в Linux fork копирует таблицы страниц, а сами страницы — лениво, copy-on- write). Переключение между процессами дороже переключения потоков: меняется адресное пространство, сбрасывается часть кэша трансляции адресов (TLB).
Изоляция против удобства. Ошибка доступа к памяти в одном потоке убивает весь процесс. Поэтому браузеры запускают вкладки отдельными процессами, а веб-серверы вроде nginx и PostgreSQL используют рабочие процессы. Обмен между процессами — через каналы (pipe), сокеты, общую память, файлы.
Связь с языками. В Java поток Thread — поток ОС (виртуальные потоки Java 21 — лёгкие, их много на одном потоке ОС). В Python потоки — тоже потоки ОС, но GIL не даёт им одновременно исполнять байт-код, поэтому для вычислений берут процессы (multiprocessing).