Concurrency and Source Panels
Mutex Counter
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
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;
}
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);for (int worker = 0; worker < 2; ++worker)
pass 1 of 219for (int worker0 = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments2, &counter0, &counterMutex(empty));21}for (int worker = 0; worker < 2; ++worker)
pass 2 of 219for (int worker1 = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments2, &counter0, &counterMutex(empty));21}for (int i = 0; i < 2; ++i)
pass 1 of 223for (int i0 = 0; i < 2; ++i) {24 workers(empty)[i0].join();25}void addSteps(int increments, int* counter, std::mutex* counterMutex)
pass 1 of 26void addSteps(int increments2, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7 for (int step = 0; step < increments; ++step) {guard ← (empty)
pass 1 of 46void 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 pass stepcounterguard1 0 ⟨addr A⟩ (empty) 2 1 ⟨addr A⟩ (empty) 3 0 — — 4 1 ⟨addr A⟩ (empty) void addSteps(int increments, int* counter, std::mutex* counterMutex)
pass 2 of 26void addSteps(int increments2, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7 for (int step = 0; step < increments; ++step) {workers[i].join();
23for (int i = 0; i < 2; ++i) {24 workers(empty)[i0].join();25}guard ← (empty)
pass 2 of 27 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 }workers[i].join();
23for (int i = 0; i < 2; ++i) {24 workers(empty)[i1].join();25}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
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);for (int worker = 0; worker < 2; ++worker)
pass 1 of 219for (int worker0 = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments1, &counter0, &counterMutex(empty));21}for (int worker = 0; worker < 2; ++worker)
pass 2 of 219for (int worker1 = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments1, &counter0, &counterMutex(empty));21}for (int i = 0; i < 2; ++i)
pass 1 of 223for (int i0 = 0; i < 2; ++i) {24 workers(empty)[i0].join();25}void addSteps(int increments, int* counter, std::mutex* counterMutex)
pass 1 of 26void addSteps(int increments1, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7 for (int step = 0; step < increments; ++step) {guard ← (empty)
pass 1 of 26void 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 }void addSteps(int increments, int* counter, std::mutex* counterMutex)
pass 2 of 26void addSteps(int increments1, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7 for (int step = 0; step < increments; ++step) {guard ← (empty)
pass 2 of 26void 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 }workers[i].join();
23for (int i = 0; i < 2; ++i) {24 workers(empty)[i0].join();25}for (int i = 0; i < 2; ++i)
pass 2 of 223for (int i1 = 0; i < 2; ++i) {24 workers(empty)[i1].join();25}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
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);for (int worker = 0; worker < 2; ++worker)
pass 1 of 219for (int worker0 = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments3, &counter0, &counterMutex(empty));21}for (int worker = 0; worker < 2; ++worker)
pass 2 of 219for (int worker1 = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments3, &counter0, &counterMutex(empty));21}void addSteps(int increments, int* counter, std::mutex* counterMutex)
pass 1 of 26void addSteps(int increments3, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7 for (int step = 0; step < increments; ++step) {guard ← (empty)
pass 1 of 66void 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 pass stepguard1 0 (empty) 2 1 (empty) 3 2 (empty) 4 0 (empty) 5 1 (empty) 6 2 (empty) counter ← 3
19for (int worker = 0; worker < 2; ++worker) {20 workers(empty).emplace_back(addSteps1, increments3, &counter→ 3, &counterMutex(empty));21}for (int i = 0; i < 2; ++i)
pass 1 of 223for (int i0 = 0; i < 2; ++i) {24 workers(empty)[i0].join();25}for (int i = 0; i < 2; ++i)
pass 2 of 223for (int i1 = 0; i < 2; ++i) {24 workers(empty)[i1].join();25}void addSteps(int increments, int* counter, std::mutex* counterMutex)
pass 2 of 26void addSteps(int increments3, int* counter⟨addr A⟩, std::mutex* counterMutex⟨addr B⟩) {7 for (int step = 0; step < increments; ++step) {workers[i].join();
23for (int i = 0; i < 2; ++i) {24 workers(empty)[i1].join();25}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