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

Может ли быть в одном процессе несколько потоков?

1.3 Junior🔥 191 комментариев
#Многопоточность и синхронизация

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

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

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

# Несколько потоков в одном процессе

Да, абсолютно — один процесс может содержать множество потоков (threads). Это фундаментальная концепция многопоточного программирования.

Архитектура процесса и потоков

Однопоточный процесс

Процесс P1
├─ Виртуальное адресное пространство (4GB or 64GB)
│  ├─ Code segment (исполняемый код)
│  ├─ Data segment (глобальные переменные)
│  ├─ Heap (динамическая память)
│  └─ Stack (локальные переменные)
├─ PID (Process ID)
├─ File descriptors (открытые файлы)
├─ Environment variables
└─ Thread 1 (main)
   ├─ Собственный Stack
   ├─ Регистры CPU
   └─ Thread-local storage (TLS)

Многопоточный процесс

Процесс P1
├─ Виртуальное адресное пространство (ОБЩЕЕ для всех потоков)
│  ├─ Code segment
│  ├─ Data segment
│  ├─ Heap (ОБЩАЯ память)
│  └─ Stacks區域
├─ PID
├─ File descriptors
├─ Environment variables
└─ НЕСКОЛЬКО потоков:
   ├─ Thread 1 (main)
   │  ├─ Stack 1
   │  ├─ Регистры TID=1001
   │  └─ TLS 1
   ├─ Thread 2 (worker)
   │  ├─ Stack 2
   │  ├─ Регистры TID=1002
   │  └─ TLS 2
   └─ Thread 3 (worker)
      ├─ Stack 3
      ├─ Регистры TID=1003
      └─ TLS 3

Что разделяют потоки?

Все потоки внутри одного процесса разделяют:

#include <thread>
#include <iostream>
#include <vector>

int global_var = 42;  // ✅ РАЗДЕЛЯЕМО: все потоки видят одну переменную

int main() {
    std::vector<int> shared_data = {1, 2, 3};  // ✅ РАЗДЕЛЯЕМО
    
    auto thread_func = [&] {
        std::cout << global_var << "\n";        // ✅ видит 42
        std::cout << shared_data[0] << "\n";   // ✅ видит 1
        
        global_var = 100;                      // ✅ изменит для всех!
        shared_data[0] = 999;                  // ✅ изменит для всех!
    };
    
    std::thread t(thread_func);
    t.join();
    
    std::cout << global_var << "\n";           // Выведет 100
    std::cout << shared_data[0] << "\n";      // Выведет 999
    
    return 0;
}

Что уникально для каждого потока?

int main() {
    std::thread t1([] {
        int local_var = 10;      // ❌ НЕ разделяется — свой stack
        static int static_var;   // ✅ РАЗДЕЛЯЕТСЯ (в data segment)
        thread_local int tls_var; // ✅ РАЗДЕЛЯЕТСЯ (но у каждого свой экземпляр)
    });
    
    std::thread t2([] {
        int local_var = 20;      // Другой local_var, другой адрес в памяти
    });
    
    t1.join();
    t2.join();
}

Практический пример: Server с многопоточностью

class WebServer {
private:
    std::vector<std::thread> worker_threads;  // пул потоков
    std::queue<Request> request_queue;         // ✅ РАЗДЕЛЯЕМАЯ очередь
    std::mutex queue_mutex;                    // синхронизация доступа
    std::condition_variable cv;                // уведомление потоков
    bool shutdown = false;                     // флаг для всех потоков
    
public:
    WebServer(int num_workers) {
        for (int i = 0; i < num_workers; ++i) {
            // Запускаем несколько потоков в одном процессе
            worker_threads.emplace_back([this] {
                worker_loop();
            });
        }
    }
    
    void worker_loop() {
        while (true) {
            std::unique_lock<std::mutex> lock(queue_mutex);
            
            // Ждём, пока придёт запрос
            cv.wait(lock, [this] {
                return !request_queue.empty() || shutdown;
            });
            
            if (shutdown && request_queue.empty()) break;
            
            Request req = request_queue.front();  // ✅ читаем из общей очереди
            request_queue.pop();
            lock.unlock();
            
            // Обрабатываем запрос (этот код выполняется параллельно!)
            Response resp = handle_request(req);
            send_response(resp);
        }
    }
    
    void handle_incoming_request(Request req) {
        std::unique_lock<std::mutex> lock(queue_mutex);
        request_queue.push(req);  // ✅ пишем в общую очередь
        lock.unlock();
        cv.notify_one();  // пробуждаем одного worker'а
    }
    
    ~WebServer() {
        {
            std::lock_guard<std::mutex> lock(queue_mutex);
            shutdown = true;
        }
        cv.notify_all();  // пробуждаем всех worker'ов
        
        for (auto& t : worker_threads) {
            t.join();  // ждём завершения всех потоков
        }
    }
};

Создание и управление потоками

#include <thread>
#include <chrono>

// Функция для потока
void worker(int id, int delay_ms) {
    std::cout << "Thread " << id << " started\n";
    std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
    std::cout << "Thread " << id << " finished\n";
}

int main() {
    // Создаём несколько потоков
    std::vector<std::thread> threads;
    
    for (int i = 1; i <= 5; ++i) {
        threads.emplace_back(worker, i, i * 100);
        // Каждый поток выполняется ПАРАЛЛЕЛЬНО
    }
    
    std::cout << "Main thread: created " << threads.size() << " threads\n";
    
    // Ждём завершения всех потоков
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "All threads completed\n";
    return 0;
}

// Вывод (порядок может варьироваться):
// Main thread: created 5 threads
// Thread 1 started
// Thread 2 started
// Thread 3 started
// Thread 4 started
// Thread 5 started
// Thread 1 finished
// Thread 2 finished
// ...
// All threads completed

Проблемы при наличии нескольких потоков

Race condition

int counter = 0;  // ✅ разделяемая переменная

void increment() {
    for (int i = 0; i < 1000000; ++i) {
        counter++;  // ❌ RACE CONDITION!
        // Операция не атомарная:
        // 1. Load counter в регистр
        // 2. Increment регистра
        // 3. Store обратно в counter
        // Потоки могут перекрываться!
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    std::cout << counter << "\n";  // Может быть меньше 2000000!
}

// Решение: atomic
std::atomic<int> counter = 0;
void increment() {
    for (int i = 0; i < 1000000; ++i) {
        counter++;  // ✅ Атомарная операция
    }
}

Deadlock

std::mutex mutex_a, mutex_b;

void thread1_func() {
    std::lock_guard<std::mutex> lock_a(mutex_a);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock_b(mutex_b);  // ждёт mutex_b
}

void thread2_func() {
    std::lock_guard<std::mutex> lock_b(mutex_b);
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    std::lock_guard<std::mutex> lock_a(mutex_a);  // ждёт mutex_a
}

int main() {
    std::thread t1(thread1_func);
    std::thread t2(thread2_func);
    
    t1.join();  // ❌ DEADLOCK — потоки ждут друг друга вечно
    t2.join();
}

Преимущества многопоточности

// ❌ Однопоточно: одно за другим
void process_sequential() {
    for (int i = 0; i < 1000000; ++i) {
        fetch_data(i);      // 100ms
        process_data(i);    // 50ms
        save_data(i);       // 100ms
        // 1 итерация = 250ms
        // 1000000 итераций = 250M ms = ~70 часов
    }
}

// ✅ Многопоточно: параллельно
void process_parallel() {
    std::thread fetcher([this] {
        for (int i = 0; i < 1000000; ++i) {
            fetch_data(i);
        }
    });
    
    std::thread processor([this] {
        for (int i = 0; i < 1000000; ++i) {
            process_data(i);
        }
    });
    
    std::thread saver([this] {
        for (int i = 0; i < 1000000; ++i) {
            save_data(i);
        }
    });
    
    fetcher.join();
    processor.join();
    saver.join();
    // 3 итерации параллельно = max(100, 50, 100) = 100ms
    // 1000000 итераций = 100M ms = ~28 часов (!!)
}

Ограничения

// Сколько потоков может быть?
int max_threads = std::thread::hardware_concurrency();  // обычно = кол-во ядер
std::cout << "CPU cores: " << max_threads << "\n";

// Но можно создать МНОГО больше (хотя это медленно):
for (int i = 0; i < 10000; ++i) {
    std::thread t([] { std::this_thread::sleep_for(std::chrono::seconds(10)); });
    t.detach();  // потоки работают независимо (опасно!)
}
// Linux создаст 10000 потоков, но переключение контекста замедлит все

Резюме

Да, один процесс может содержать множество потоков.

Основные моменты:

  1. Разделяют: код, глобальные переменные, heap, файлы, PID
  2. Не разделяют: stack, регистры, TLS (thread-local storage)
  3. Выполняются параллельно (на многоядерных системах) или чередуются (на одноядерных)
  4. Требуют синхронизации (mutexes, condition variables, atomic)
  5. Опасны: race conditions, deadlocks, используй инструменты для отладки (ThreadSanitizer)

Современные backend системы используют множество потоков для обработки параллельных запросов, асинхронных операций и масштабирования на мультиядерных ЦПУ.