Search code examples
c++atomicstdatomic

In the example of std::atomic<T>::exchange, why the count of times is not 25?


The example I talked about is this one on cppreference.com. The code snippet is pasted below.

int main(){
    const std::size_t ThreadNumber = 5;
    const int Sum = 5;
    std::atomic<int> atom{0};
    std::atomic<int> counter{0};
 
    // lambda as thread proc
    auto lambda = [&](const int id){
        for (int next = 0; next < Sum;){
            // each thread is writing a value from its own knowledge
            const int current = atom.exchange(next);
            counter++;
            // sync writing to prevent from interrupting by other threads
            std::osyncstream(std::cout)
                << '#' << id << " (" << std::this_thread::get_id()
                << ") wrote " << next << " replacing the old value "
                << current << '\n';
            next = std::max(current, next) + 1;
        }
    };
 
    std::vector<std::thread> v;
    for (std::size_t i = 0; i < ThreadNumber; ++i){
        v.emplace_back(lambda, i);
    }
 
    for (auto& tr : v){
        tr.join();
    }
 
    std::cout << ThreadNumber << " threads adding 0 to "
              << Sum << " takes total "
              << counter << " times\n";
}

To me, the value of counter is 25 because 5 threads and each thread loops 5 times. However, the shown output is 16. I also ran it myself, the possible value varies, but it never gets to be 25.

Why the printed value of counter is actually smaller?


Solution

  • Consider one of the possible executions:

    Lets say one of the threads finishes the loop before other threads start.

    This gives you atom == 4. The next thread to enter the loop will get current == 4 and will exit the loop after the first iteration.

    This way the second thread increments current once instead of 5 times like you expect it to.