Страницы

Поиск по вопросам

суббота, 21 декабря 2019 г.

в чем проблема с отсутствием мьютексов в коде?

#cpp #многопоточность #cpp11


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

struct Counter {
    int value;

    Counter() : value(0){}

    void increment(){
        ++value;
    }
};

int main() {
    Counter counter;

    std::vector threads;
    for(int i = 0; i < 5; ++i){
        threads.push_back(std::thread([&counter](){
            for(int i = 0; i < 100; ++i){
                counter.increment();
            }
        }));
    }

    for(auto& thread : threads){
        thread.join();
    }

    std::cout << counter.value << std::endl;

    return 0;
}


совершенно не могу понять почему вылетают разные значения... у нас ведь join() т.е.
мы ждем когда каждый из пяти потоков выполнится один за другим(т.е. инкрементирует
по 100 раз каждый), я понимаю если б detach() стоял, то была бы жесть... а так каждый
поток из пяти один за другим ведь следом идут... почему значения отличные от 500 получаются.
либо я чего-то не учитываю... разве не верно, что в первом потоке будет 100, и в последующих
будут на 100 больше?
    


Ответы

Ответ 1



У Вас не синхронизирован доступ к counter, поэтому, в силу data races, как Вам уже правильно указал VladD, у Вас в коде UB. Чтобы исправить код, Вам нужно синхронизировать доступ к counter внутри, или снаружи. Можно сделать так: struct Counter { std::atomic_int value; ... }; Или так: std::mutex guard; std::vector threads; for(int i = 0; i < 5; ++i) { threads.push_back(std::thread([&counter, &guard]() { for(int i = 0; i < 100; ++i) { std::lock_guard lock{guard}; counter.increment(); } })); } Или ещё массой других вариантов — главное, чтобы пропали гонки(data races)

Ответ 2



Ваша главная ошибка вот: у нас ведь join() т.е. мы ждем когда каждый из пяти потоков выполнится один за другим Отнюдь. Выполняться они могут как угодно. Вы ожидаете не выполнения, а завершения. Грубо говоря, возможна ситуация, когда первый поток выполнится последним, так что остальные join'ы будут к уже отработавшим потокам. Этот цикл - просто гарантия того, что отработают все потоки, а уж в каком порядке они будут работать и как - на это ваши join'ы никак не влияют.

Комментариев нет:

Отправить комментарий