C ++ Thread не обнаруживает изменение глобальной переменной - PullRequest
2 голосов
/ 29 мая 2020

У меня есть 2 потока, отслеживающих один и тот же глобальный state, если state.shutdown становится false, поток run() должен вернуться. Код ниже.

#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>

using namespace std;

struct State {
    bool shutdown = false;

    ~State() {
        shutdown = true;
    }
};

State state;

#define CHECK_SHUTDOWN     \
  {                        \
    std::cout << (state.shutdown ? " SHUTDOWN " : " NOSHUT ") << typeid(*this).name() << std::endl; \
    if (state.shutdown) {  \
        return;            \
    }                      \
  }

class Mythread {
public:
    void join();
    void run();
    void launch();
    std::thread self_thread;
};

void Mythread::run() {
    while(1) {
        CHECK_SHUTDOWN
    }
}

void Mythread::join() {
    if (self_thread.joinable()) {
        self_thread.join();
    }
}

void Mythread::launch() {
    self_thread = std::thread(&Mythread::run, this);
}

std::mutex mtx;
void shut() {
    std::lock_guard<std::mutex> lock(mtx);   
    state.shutdown = true;
}

int main()
{
    Mythread thread1;
    Mythread thread2;

    thread1.launch();
    thread2.launch();

    std::this_thread::sleep_for(std::chrono::milliseconds(1000));


    //state.shutdown = true;
    shut(); //This makes no difference with the line above

    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    thread1.join();
    thread2.join();


    return 0;
}

Однако, даже если я вручную установил значение state.shutdown как истинное, потоки никогда не смогут его обнаружить. Я получил отпечатки вроде:

 NOSHUT 8Mythread                                                                                                
 NOSHUT 8Mythread                                                                                                
 NOSHUT 8Mythread                                                                                                                                                                                                                

...Program finished with exit code 0                                                                             
Press ENTER to exit console. 

в конце. Я также смущен, учитывая, что функция run() никогда не возвращается, соединение потоков должно зависнуть. Однако потоки могут успешно присоединиться.

Любая помощь будет очень принята здесь!

1 Ответ

1 голос
/ 29 мая 2020

У вас гонка данных при завершении работы.

Когда оценка выражения записывается в ячейку памяти, а другая оценка считывает или изменяет ту же ячейку памяти, выражения говорят, что конфликтуют. Программа, которая имеет две конфликтующие оценки, имеет гонку данных [...]

В shut() вы устанавливаете флаг shutdown с помощью мьютекса, но проверка выполняется без мьютекс (и деструктор State также не использует мьютекс). Таким образом, у вас есть конфликтующие операции (чтение + запись) с переменной, отличной от atomi c, без правильного отношения «происходит до». Это гонка данных, которая приводит к неопределенному поведению.

Простым решением было бы сделать shutdown an std::atomic<bool>, тогда вам даже не понадобится мьютекс для установки флага.

Для получения дополнительной информации о гонках данных и модели памяти C ++ я могу порекомендовать эту статью, в соавторстве которой я являюсь: Модели памяти для программистов на C / C ++

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...