Обновления мьютексов и переменных

Требуется проверка мыслительного процесса, скажем, я создал тему следующим образом

bool threadRun=true;
std::mutex threadMutex;
std::condition_variable event;
std::thread processThread(process);

void process()
{
    while(threadRun)
    {
        if(noWork())
        {
            std::unique_lock<std::mutex> lock(threadMutex);

            if(threadRun)
            {
                if(!getWork()) //try to get work, return false if no work
                    event.wait(lock);
            }

            continue;
        }

        doWork();
    }
}

void stopThread()
{
    {
        std::unique_lock<std::mutex> lock(threadMutex);
        threadRun=false;
    }
    event.notify_all();
    processThread.join();
}

Поскольку threadRun не находится под блокировкой мьютекса в потоке, нет гарантии, что вызов stopThread фактически остановит поток, поскольку при наличии работы не требуется очистка кеша. Однако, когда нет работы и блокировка взята, это должно вызвать очистка кеша и threadRun гарантированно обновляются, правильно?

Гонка за данными - это гонка за данными, почему, как вы думаете, очистка кеша имеет к этому какое-то отношение?

463035818_is_not_a_number 13.09.2018 21:30
threadRun будет читаться без синхронизации. Это проблема, которую нужно исправить. Нет необходимости вводить системные детали, такие как очистка кеша. Абстрактная машина позаботится о том, чтобы все работало, если вы подчиняетесь правилам языка.
François Andrieux 13.09.2018 21:37

Если вы ищете быстрое исправление, вы можете сделать threadRunstd::atomic_bool.

François Andrieux 13.09.2018 21:44
1
3
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Гонки данных в C++ - это неопределенное поведение.

Теперь вы можете подумать, что это означает: «Хорошо, я не против, что они пропустили чтение, в конце концов они его получат». Но UB - это то, что компилятор может принять не случится.

Тот факт, что вы читаете threadRun без блокировки или другой синхронизации, означает, что компилятор может, может и должен предполагать, что никто в любом другом потоке не будет изменять threadRun.

Таким образом, код можно оптимизировать для:

if(threadRun)
  while(true)

т.е. компилятор может прочитать threadRun один раз, а затем не утруждать себя чтением его снова, если он может доказать, что никто не может изменить его значение определенным образом.

Теперь, если компилятор не может этого доказать, он должен прочитать threadRun. Таким образом, ваш UB действует скорее так, как будто он «должен» работать. А на некоторых платформах само оборудование кэшировало threadRun в кеш-памяти каждого процессора (или потока), и тот факт, что другой поток написал в него, не отражается в вашем рабочем потоке в течение некоторого неизвестного периода времени (возможно, навсегда). .

Идя дальше, умный компилятор может заметить, что вы читаете threadRun без синхронизации. Затем он мог использовать это, чтобы доказать, что никто, с синхронизацией или без нее, не может писать на threadRun в другом потоке.

Как только он это докажет, он также может исключить проверку threadRun внутри мьютекса.

Не выполняйте неопределенное поведение, если вы не хотите проверять сборку, создаваемую вашей программой с этого момента до конца времени, каждый раз, когда вы ее компилируете, даже если она «работает» или выигрыш огромен и стоит риска.

Замените threadRun на std::atomic_bool. В худшем случае вы потеряете крошечную долю производительности и получите правильность.

спасибо, просто нужно было ударить по голове битой UB для напоминания.

Krazer 13.09.2018 22:09

Другие вопросы по теме