Protect a shared counter with a mutex when multiple threads update it.

lock `std::lock_guard` holds the mutex for the rest of the current scope.
total The final count is deterministic because every increment happens while the mutex is held.

Mutex Counter

increments
mutex_counter.cpp
Replay: real traced execution (multi-file project)
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

void addSteps(int increments, int* counter, std::mutex* counterMutex) {
    for (int step = 0; step < increments; ++step) {
        std::lock_guard<std::mutex> guard(*counterMutex);
        *counter += 1;
    }
}

int main() {
    int increments = 2;
    int counter = 0;
    std::mutex counterMutex;
    std::vector<std::thread> workers;

    for (int worker = 0; worker < 2; ++worker) {
        workers.emplace_back(addSteps, increments, &counter, &counterMutex);
    }

    for (int i = 0; i < 2; ++i) {
        workers[i].join();
    }

    std::cout << "increments=" << increments << std::endl;
    std::cout << "counter=" << counter << std::endl;
    return 0;
}
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

void addSteps(int increments, int* counter, std::mutex* counterMutex) {
    for (int step = 0; step < increments; ++step) {
        std::lock_guard<std::mutex> guard(*counterMutex);
        *counter += 1;
    }
}

int main() {
    int increments = 1;
    int counter = 0;
    std::mutex counterMutex;
    std::vector<std::thread> workers;

    for (int worker = 0; worker < 2; ++worker) {
        workers.emplace_back(addSteps, increments, &counter, &counterMutex);
    }

    for (int i = 0; i < 2; ++i) {
        workers[i].join();
    }

    std::cout << "increments=" << increments << std::endl;
    std::cout << "counter=" << counter << std::endl;
    return 0;
}
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

void addSteps(int increments, int* counter, std::mutex* counterMutex) {
    for (int step = 0; step < increments; ++step) {
        std::lock_guard<std::mutex> guard(*counterMutex);
        *counter += 1;
    }
}

int main() {
    int increments = 3;
    int counter = 0;
    std::mutex counterMutex;
    std::vector<std::thread> workers;

    for (int worker = 0; worker < 2; ++worker) {
        workers.emplace_back(addSteps, increments, &counter, &counterMutex);
    }

    for (int i = 0; i < 2; ++i) {
        workers[i].join();
    }

    std::cout << "increments=" << increments << std::endl;
    std::cout << "counter=" << counter << std::endl;
    return 0;
}
  1. increments ← 2, counter ← 0, counterMutex ← (empty), workers ← (empty)

    13int main() {14    int increments→ 2 = 2; //@increments=1, 315    int counter→ 0 = 0;16    std::mutex counterMutex→ (empty);17    std::vector<std::thread> workers→ (empty);
  2. for (int worker = 0; worker < 2; ++worker)

    pass 1 of 2
    19for (int worker0 = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments2, &counter0, &counterMutex(empty));21}
  3. for (int worker = 0; worker < 2; ++worker)

    pass 2 of 2
    19for (int worker1 = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments2, &counter0, &counterMutex(empty));21}
  4. for (int i = 0; i < 2; ++i)

    pass 1 of 2
    23for (int i0 = 0; i < 2; ++i) {24    workers(empty)[i0].join();25}
  5. void addSteps(int increments, int* counter, std::mutex* counterMutex)

    pass 1 of 2
    6void addSteps(int increments2, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7    for (int step = 0; step < increments; ++step) {
  6. guard ← (empty)

    pass 1 of 4
    6void addSteps(int increments, int* counter, std::mutex* counterMutex) {7    for (int step0 = 0; step < increments2; ++step) {8        std::lock_guard<std::mutex> guard→ (empty)(*counterMutex⟨addr B⟩);9        *counter⟨addr A⟩ += 1;10    }
    All 4 passes — pass 1 is the card above
    passstepcounterguard
    10⟨addr A⟩(empty)
    21⟨addr A⟩(empty)
    30
    41⟨addr A⟩(empty)
  7. void addSteps(int increments, int* counter, std::mutex* counterMutex)

    pass 2 of 2
    6void addSteps(int increments2, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7    for (int step = 0; step < increments; ++step) {
  8. workers[i].join();

    23for (int i = 0; i < 2; ++i) {24    workers(empty)[i0].join();25}
  9. guard ← (empty)

    pass 2 of 2
    7    for (int step = 0; step < increments; ++step) {8        std::lock_guard<std::mutex> guard→ (empty)(*counterMutex⟨addr B⟩);9        *counter⟨addr A⟩ += 1;10    }11}1213int main() {14    int increments = 2; //@increments=1, 315    int counter = 0;16    std::mutex counterMutex;17    std::vector<std::thread> workers;1819    for (int worker = 0; worker < 2; ++worker) {20        workers.emplace_back(addSteps, increments, &counter, &counterMutex);21    }2223    for (int i1 = 0; i < 2; ++i) {24        workers(empty)[i1].join();25    }
  10. workers[i].join();

    23for (int i = 0; i < 2; ++i) {24    workers(empty)[i1].join();25}
  11. std::cout << "increments=" << increments << std::endl;

    27    std::cout << "increments=" << increments2 << std::endl;28    std::cout << "counter=" << counter4 << std::endl;29    return 0;30}
    outputincrements=2
    counter=4
  1. increments ← 1, counter ← 0, counterMutex ← (empty), workers ← (empty)

    13int main() {14    int increments→ 1 = 1;15    int counter→ 0 = 0;16    std::mutex counterMutex→ (empty);17    std::vector<std::thread> workers→ (empty);
  2. for (int worker = 0; worker < 2; ++worker)

    pass 1 of 2
    19for (int worker0 = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments1, &counter0, &counterMutex(empty));21}
  3. for (int worker = 0; worker < 2; ++worker)

    pass 2 of 2
    19for (int worker1 = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments1, &counter0, &counterMutex(empty));21}
  4. for (int i = 0; i < 2; ++i)

    pass 1 of 2
    23for (int i0 = 0; i < 2; ++i) {24    workers(empty)[i0].join();25}
  5. void addSteps(int increments, int* counter, std::mutex* counterMutex)

    pass 1 of 2
    6void addSteps(int increments1, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7    for (int step = 0; step < increments; ++step) {
  6. guard ← (empty)

    pass 1 of 2
    6void addSteps(int increments, int* counter, std::mutex* counterMutex) {7    for (int step0 = 0; step < increments1; ++step) {8        std::lock_guard<std::mutex> guard→ (empty)(*counterMutex⟨addr B⟩);9        *counter⟨addr A⟩ += 1;10    }
  7. void addSteps(int increments, int* counter, std::mutex* counterMutex)

    pass 2 of 2
    6void addSteps(int increments1, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7    for (int step = 0; step < increments; ++step) {
  8. guard ← (empty)

    pass 2 of 2
    6void addSteps(int increments, int* counter, std::mutex* counterMutex) {7    for (int step0 = 0; step < increments1; ++step) {8        std::lock_guard<std::mutex> guard→ (empty)(*counterMutex⟨addr B⟩);9        *counter⟨addr A⟩ += 1;10    }
  9. workers[i].join();

    23for (int i = 0; i < 2; ++i) {24    workers(empty)[i0].join();25}
  10. for (int i = 0; i < 2; ++i)

    pass 2 of 2
    23for (int i1 = 0; i < 2; ++i) {24    workers(empty)[i1].join();25}
  11. std::cout << "increments=" << increments << std::endl;

    27    std::cout << "increments=" << increments1 << std::endl;28    std::cout << "counter=" << counter2 << std::endl;29    return 0;30}
    outputincrements=1
    counter=2
  1. increments ← 3, counter ← 0, counterMutex ← (empty), workers ← (empty)

    13int main() {14    int increments→ 3 = 3;15    int counter→ 0 = 0;16    std::mutex counterMutex→ (empty);17    std::vector<std::thread> workers→ (empty);
  2. for (int worker = 0; worker < 2; ++worker)

    pass 1 of 2
    19for (int worker0 = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments3, &counter0, &counterMutex(empty));21}
  3. for (int worker = 0; worker < 2; ++worker)

    pass 2 of 2
    19for (int worker1 = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments3, &counter0, &counterMutex(empty));21}
  4. void addSteps(int increments, int* counter, std::mutex* counterMutex)

    pass 1 of 2
    6void addSteps(int increments3, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7    for (int step = 0; step < increments; ++step) {
  5. guard ← (empty)

    pass 1 of 6
    6void addSteps(int increments, int* counter, std::mutex* counterMutex) {7    for (int step0 = 0; step < increments3; ++step) {8        std::lock_guard<std::mutex> guard→ (empty)(*counterMutex⟨addr B⟩);9        *counter⟨addr A⟩ += 1;10    }
    All 6 passes — pass 1 is the card above
    passstepguard
    10(empty)
    21(empty)
    32(empty)
    40(empty)
    51(empty)
    62(empty)
  6. counter ← 3

    19for (int worker = 0; worker < 2; ++worker) {20    workers(empty).emplace_back(addSteps1, increments3, &counter→ 3, &counterMutex(empty));21}
  7. for (int i = 0; i < 2; ++i)

    pass 1 of 2
    23for (int i0 = 0; i < 2; ++i) {24    workers(empty)[i0].join();25}
  8. for (int i = 0; i < 2; ++i)

    pass 2 of 2
    23for (int i1 = 0; i < 2; ++i) {24    workers(empty)[i1].join();25}
  9. void addSteps(int increments, int* counter, std::mutex* counterMutex)

    pass 2 of 2
    6void addSteps(int increments3, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7    for (int step = 0; step < increments; ++step) {
  10. workers[i].join();

    23for (int i = 0; i < 2; ++i) {24    workers(empty)[i1].join();25}
  11. std::cout << "increments=" << increments << std::endl;

    27    std::cout << "increments=" << increments3 << std::endl;28    std::cout << "counter=" << counter6 << std::endl;29    return 0;30}
    outputincrements=3
    counter=6