Почему фрагмент кода зависает при включенной оптимизации?

Есть три вопроса о фрагменте кода ниже.

  1. Когда макрос (например, NO_STUCK_WITH_OPTIMIZATION ) не включен, почему этот фрагмент кода зависает при включенной оптимизации (например, -O1, -O2 или -O3), тогда как программа работает хорошо, если оптимизация не включена?
  2. И почему программа больше не зависает, если добавить std::this_thread::sleep_for()?

ОБНОВЛЕНО: 3. Если is_run объявить как volatile(подробнее см. фрагмент кода), то программа никогда не застрянет на на X86? ^^ОБНОВЛЕНО КОНЕЦ^^

    #include <functional>
    #include <thread>
    #include <iostream>
    #include <chrono>
    #include <atomic>
    
    #ifdef NO_STUCK_WITH_OPTIMIZATION 
    using TYPE = std::atomic<int>;
    #else
    using TYPE = int;  //The progrom gets stuck if the optimization is enabled.
    #endif
    
    int main()
    {
        TYPE is_run{1};
        auto thread = std::thread([&is_run](){while(1==is_run){
              //std::this_thread::sleep_for(std::chrono::milliseconds(10)); //If this line is added, the program is no longer gets stuck. Why?
                                            }
            std::cout << "thread game over" << std::endl;
        });
    
        std::this_thread::sleep_for(std::chrono::seconds(1));
        is_run = 0;
        thread.join();
    }

Просто чтобы убедиться, что ваш вопрос понят, вы спрашиваете: «Почему, когда я использую не потокобезопасные объекты с несколькими потоками выполнения, мой код оказывается сломанным»?

Sam Varshavchik 03.04.2022 15:05

См. en.cppreference.com/w/cpp/language/…

Passer By 03.04.2022 15:05

Извините за мой плохой английский. Мой заголовок надеется указать на два вопроса. Во-первых, почему фрагмент кода зависает, когда оптимизация включена, а программа работает хорошо, если оптимизация не включена. Во-вторых, почему фрагмент кода больше не зависает, если добавить std::this_thread::sleep_for.

John 03.04.2022 15:07

Потому что именно это означает «неопределенное поведение». Без надлежащей безопасности резьбы все гарантии аннулируются. Возврата не предусмотрено. Пусть покупатель будет бдителен. Ты сам по себе. Программа может работать или не работать, а работает она или нет, зависит от параметров компиляции, времени суток, фазы луны, текущей погоды или любого другого фактора. Очень мало значения имеет причина конкретного проявления неопределенного поведения. Это неопределенное поведение.

Sam Varshavchik 03.04.2022 15:10
Почему фрагмент кода зависает при включенной оптимизации? Потому что в программе есть ошибка. Он использует переменную в двух потоках без координации между потоками, такой как атомарный или мьютекс.
Eljay 03.04.2022 15:30
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

У вас многопоточная программа. Одна нить делает is_run = 0;.

Другой поток делает while(1==is_run). Хотя вы гарантируете со сном (это вопрос для другого вопроса), что запись выполняется до чтения, вам нужно указать компилятору синхронизировать эту переменную.

В C++ простой способ убедиться, что один поток увидит изменение, — это использовать atomic<int>. Если вы этого не сделаете, другой поток может никогда не увидеть изменения. Это сокрытие изменения может быть связано с локальной оптимизацией кода во время компиляции, решением ОС не обновлять какую-либо страницу памяти или самим аппаратным обеспечением, решившим, что память не нужно перезагружать.

Использование атомарной переменной гарантирует, что все эти системы знают, что вы хотите сделать. Итак, используйте его. :-)

Поднятие из комментариев:

https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races

A program that has two conflicting evaluations has a data race unless both evaluations execute on the same thread or in the same signal handler, or both conflicting evaluations are atomic operations (see std::atomic), or one of the conflicting evaluations happens-before another (see std::memory_order) If a data race occurs, the behavior of the program is undefined.

Если is_run объявлен как volatile(подробнее см. фрагмент кода), то программа никогда не застрянет на на X86?

John 03.04.2022 16:59

@John Да, с volatile компилятору не разрешено оптимизировать чтение, поэтому программа будет работать на x86. Но я думаю, что программа все еще технически неопределенное поведение. Она может дать сбой на платформах с более слабыми моделями памяти. Не используйте volatile для многопоточности, только std::atomic или около того.

prapin 03.04.2022 17:29

да, не используйте volatile. Это ключевое слово было добавлено до того, как в C++ появилась модель памяти. Это устранит некоторые проблемы (предотвратив некоторую оптимизацию компилятора), но не все, и ваш код все равно будет иметь UB.

Jeffrey 03.04.2022 18:14

@Jeffrey Джеффри, я создаю новый вопрос, давайте обсудим на здесь.

John 04.04.2022 05:00

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