В чём разница между процессом и потоком?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
В чём разница между процессом и потоком?
Процесс (Process) и поток (Thread) — это разные уровни параллелизма в операционной системе. Процесс — это независимый экземпляр программы со своей памятью, файлами и ресурсами. Поток — это легковесная единица выполнения внутри процесса, которая делит память и ресурсы с другими потоками.
Структура памяти
Процесс имеет собственное адресное пространство:
Процесс 1 Процесс 2
───────────── ──────────────
| Code | | Code | (не могут видеть
| Data | | Data | друг друга)
| Heap | | Heap | (разные указатели
| Stack | | Stack | на одну память = разные)
Программа foo.exe запущена дважды = 2 разных процесса. Каждый видит свою память.
Потоки внутри процесса делят память:
Процесс
──────────────────────
| Code |
| Data | <- общие для всех потоков
| Heap |
├─────────────────────
| Stack1 | Thread1 | каждый поток
| Stack2 | Thread2 | имеет свой стек
| Stack3 | Thread3 |
Все потоки одного процесса видят одну копию heap и data секций. Это как несколько людей работают в одном офисе (процесс) и делят стол (память).
Сравнение по параметрам
Параметр Процесс Поток
────────────────────────────────────────────────────
Адресное пространство Отдельное Общее с другими
Память (Code/Data) Отдельная Общая (shared)
Стек (Stack) Отдельный Отдельный
Ресурсы (файлы, ...) Отдельные Общие
Коммуникация Сложная (IPC) Простая (shared memory)
Контекстное Дорогое ~μs Дешёвое ~нано
переключение
Изоляция Отличная Слабая (race conditions)
Время создания Медленно (мс) Быстро (μs)
Ошибка в одном Не влияет Может убить весь процесс
Контекстное переключение
Процесс:
ОС сохраняет/восстанавливает при переключении:
1. Регистры CPU
2. Счётчик команд (PC)
3. Указатель стека (SP)
4. Таблица виртуальной памяти (TLB flush)
5. Дескрипторы файлов
6. Права доступа
Время: ~1-10 микросекунд
Поток:
ОС сохраняет только:
1. Регистры CPU
2. Счётчик команд
3. Указатель стека
Таблица памяти не меняется (та же!)
Время: ~100-500 наносекунд (в 10-100 раз быстрее)
Race Condition — основная проблема потоков
int counter = 0; // Shared между потоками
void increment() {
counter++; // НЕ атомарно!
}
// Thread 1 и Thread 2 одновременно:
// Операция counter++ = 3 шага:
// 1. Читай counter (value = 0)
// 2. Прибавь 1 (temp = 1)
// 3. Запиши обратно (counter = 1)
// Без синхронизации может быть такая последовательность:
// Thread1: Read (0) ──┐
// Thread2: Read (0) ──┐
// Thread1: Inc ──┐
// Thread2: Inc ──┐
// Thread1: Write (1)
// Thread2: Write (1)
// Результат: counter = 1 вместо 2 ! УТЕЧКА ИНКРЕМЕНТА!
Решение с мьютексом:
std::mutex mtx;
int counter = 0;
void increment() {
std::lock_guard<std::mutex> lock(mtx); // Блокировка
counter++; // Теперь безопасно
} // Автоматически разблокируется
Или атомарно:
std::atomic<int> counter(0);
void increment() {
counter++; // Атомарная операция, безопасно
}
Создание процесса vs потока
Процесс (Unix/Linux):
#include <unistd.h>
int main() {
pid_t pid = fork(); // Создаём новый процесс
if (pid == 0) {
// Дочерний процесс
std::cout << "Child process" << std::endl;
exit(0);
} else if (pid > 0) {
// Родительский процесс
std::cout << "Parent process, child PID = " << pid << std::endl;
wait(nullptr); // Ждём дочерний процесс
}
}
Поток (C++11):
#include <thread>
void worker() {
std::cout << "Worker thread" << std::endl;
}
int main() {
std::thread t(worker); // Создаём поток
t.join(); // Ждём завершения потока
}
Коммуникация между процессами (IPC)
// Процессы не могут напрямую писать в память друг друга
// Нужны специальные механизмы:
// 1. Pipes (конвейеры)
int pipefd[2];
pipe(pipefd);
// pipefd[0] - для чтения, pipefd[1] - для писания
// 2. Shared Memory (разделяемая память)
int shmid = shmget(key, size, IPC_CREAT);
void* addr = shmat(shmid, nullptr, 0);
// 3. Sockets (сокеты)
socket(AF_INET, SOCK_STREAM, 0);
// 4. Message Queues
msgget(key, IPC_CREAT);
Потоки проще:
int shared_var = 0; // Видна всем потокам
void thread_func() {
shared_var++; // Просто читаем/пишем
}
Примеры использования
Процессы нужны когда:
- Изоляция критична (безопасность, стабильность)
- Запускаешь внешние программы
- Долгоживущие воркеры с разными ресурсами
- Крах одного не должен убить другие
// Web-сервер: каждый клиент в отдельном процессе
while (true) {
int client_socket = accept(...);
pid_t pid = fork();
if (pid == 0) {
handle_client(client_socket); // Отдельный процесс
exit(0);
}
}
Потоки нужны когда:
- Быстрая коммуникация между работниками
- Общее состояние (куча, данные)
- Частые переключения контекста
- Высокая производительность
// Thread pool для обработки задач
std::vector<std::thread> workers;
std::queue<Task> task_queue; // Общая очередь
std::mutex queue_mutex;
for (int i = 0; i < 4; i++) {
workers.emplace_back([&] {
while (true) {
std::lock_guard<std::mutex> lock(queue_mutex);
if (task_queue.empty()) continue;
Task t = task_queue.front();
task_queue.pop();
process(t);
}
});
}
Современный подход
Асинхронное программирование (Futures, async):
#include <future>
int main() {
// Не создаём явно потоки, пусть STL разбирается
std::future<int> result = std::async(std::launch::async, []
{
return expensive_computation();
});
int value = result.get(); // Ждём результат
}
Резюме для собеседования
Процесс — тяжёлый, изолированный, со своей памятью. Медленное переключение (~μs), безопасно от ошибок друг друга.
Поток — лёгкий, общая память, быстрое переключение (~нс). Нужна синхронизация (мьютексы, atomic).
Выбор: По умолчанию потоки (быстрее, проще), но если нужна изоляция → процессы.