Все ли потоки имеют доступ к глобальной переменной?

«Все ли потоки имеют доступ к глобальной переменной?» — вопрос из категории Многопоточность, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, все потоки, созданные в рамках одного процесса, имеют доступ к глобальным (статическим) переменным, так как они находятся в общем адресном пространстве. Однако такой несинхронизированный доступ приводит к состоянию гонки (race condition). Для безопасной работы с общими данными необходимо использовать механизмы синхронизации.

Основные подходы в C++:

  1. Мьютексы (std::mutex): Гарантируют эксклюзивный доступ.
  2. Атомарные операции (std::atomic): Позволяют выполнять операции чтения-записи без блокировок для примитивных типов.
  3. Условные переменные (std::condition_variable): Для координации потоков по событиям.

Пример с std::mutex и RAII-оберткой std::lock_guard:

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

int global_counter = 0;
std::mutex global_counter_mutex;

void increment_global(int iterations) {
    for (int i = 0; i < iterations; ++i) {
        // Автоматически блокирует и разблокирует мьютекс
        std::lock_guard<std::mutex> lock(global_counter_mutex);
        ++global_counter; // Критическая секция защищена
    }
}

int main() {
    std::thread t1(increment_global, 100000);
    std::thread t2(increment_global, 100000);

    t1.join();
    t2.join();

    // Без мьютекса результат был бы меньше 200000
    std::cout << "Final counter value: " << global_counter << std::endl; // 200000
    return 0;
}

Почему это важно: Без синхронизации операция ++global_counter (которая состоит из чтения, изменения и записи) может быть прервана другим потоком, что ведет к потере обновлений и неопределенному поведению программы.