Use fixed result slots when worker threads produce values independently.

slots Each thread writes to a different vector slot, so the final join can summarize the results without shared mutation on one counter.
replay The replay panel can switch between thread IDs as the worker events interleave.

Thread Result Slots

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

void fillSlot(int slot, int* results) {
    int value = (slot + 1) * 10;
    results[slot] = value;
}

int main() {
    int workerCount = 2;
    int results[3] = {0, 0, 0};
    std::vector<std::thread> workers;

    for (int i = 0; i < workerCount; ++i) {
        workers.emplace_back(fillSlot, i, results);
    }

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

    int total = 0;
    for (int i = 0; i < workerCount; ++i) {
        total += results[i];
    }

    std::cout << "workers=" << workerCount << std::endl;
    std::cout << "total=" << total << std::endl;
    return 0;
}
#include <iostream>
#include <thread>
#include <vector>

void fillSlot(int slot, int* results) {
    int value = (slot + 1) * 10;
    results[slot] = value;
}

int main() {
    int workerCount = 1;
    int results[3] = {0, 0, 0};
    std::vector<std::thread> workers;

    for (int i = 0; i < workerCount; ++i) {
        workers.emplace_back(fillSlot, i, results);
    }

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

    int total = 0;
    for (int i = 0; i < workerCount; ++i) {
        total += results[i];
    }

    std::cout << "workers=" << workerCount << std::endl;
    std::cout << "total=" << total << std::endl;
    return 0;
}
#include <iostream>
#include <thread>
#include <vector>

void fillSlot(int slot, int* results) {
    int value = (slot + 1) * 10;
    results[slot] = value;
}

int main() {
    int workerCount = 3;
    int results[3] = {0, 0, 0};
    std::vector<std::thread> workers;

    for (int i = 0; i < workerCount; ++i) {
        workers.emplace_back(fillSlot, i, results);
    }

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

    int total = 0;
    for (int i = 0; i < workerCount; ++i) {
        total += results[i];
    }

    std::cout << "workers=" << workerCount << std::endl;
    std::cout << "total=" << total << std::endl;
    return 0;
}
  1. workerCount ← 2, results ← ⟨addr A⟩, workers ← (empty)

    10int main() {11    int workerCount→ 2 = 2; //@workerCount=1, 312    int results→ ⟨addr A⟩[3] = {0, 0, 0};13    std::vector<std::thread> workers→ (empty);
  2. for (int i = 0; i < workerCount; ++i)

    pass 1 of 2
    15for (int i0 = 0; i < workerCount2; ++i) {16    workers(empty).emplace_back(fillSlot1, i0, results⟨addr A⟩);17}
  3. for (int i = 0; i < workerCount; ++i)

    pass 2 of 2
    15for (int i1 = 0; i < workerCount2; ++i) {16    workers(empty).emplace_back(fillSlot1, i1, results⟨addr A⟩);17}
  4. for (int i = 0; i < workerCount; ++i)

    pass 1 of 2
    19for (int i0 = 0; i < workerCount2; ++i) {20    workers(empty)[i0].join();21}
  5. value ← 20, results[slot] ← 20

    pass 1 of 2
    5void fillSlot(int slot1, int* results⟨addr A⟩) {6    int value→ 20 = (slot1 + 1) * 10;7    results[slot]→ 20 = value20;8}
  6. value ← 10, results[slot] ← 10

    pass 2 of 2
    5void fillSlot(int slot0, int* results⟨addr A⟩) {6    int value→ 10 = (slot0 + 1) * 10;7    results[slot]→ 10 = value10;8}
  7. workers[i].join();

    19for (int i = 0; i < workerCount; ++i) {20    workers(empty)[i0].join();21}
  8. for (int i = 0; i < workerCount; ++i)

    pass 2 of 2
    19for (int i1 = 0; i < workerCount2; ++i) {20    workers(empty)[i1].join();21}
  9. total ← 0

    23int total→ 0 = 0;24for (int i = 0; i < workerCount; ++i) {
  10. total ← 10

    pass 1 of 2
    23int total = 0;24for (int i0 = 0; i < workerCount2; ++i) {25    total→ 10 += results[i]10;26}
  11. total ← 30

    pass 2 of 2
    23int total = 0;24for (int i1 = 0; i < workerCount2; ++i) {25    total→ 30 += results[i]20;26}
  12. std::cout << "workers=" << workerCount << std::endl;

    28    std::cout << "workers=" << workerCount2 << std::endl;29    std::cout << "total=" << total30 << std::endl;30    return 0;31}
    outputworkers=2
    total=30
  1. workerCount ← 1, results ← ⟨addr A⟩, workers ← (empty)

    10int main() {11    int workerCount→ 1 = 1;12    int results→ ⟨addr A⟩[3] = {0, 0, 0};13    std::vector<std::thread> workers→ (empty);
  2. for (int i = 0; i < workerCount; ++i)

    15for (int i0 = 0; i < workerCount1; ++i) {16    workers(empty).emplace_back(fillSlot1, i0, results⟨addr A⟩);17}
  3. for (int i = 0; i < workerCount; ++i)

    19for (int i0 = 0; i < workerCount1; ++i) {20    workers(empty)[i0].join();21}
  4. value ← 10, results[slot] ← 10

    5void fillSlot(int slot0, int* results⟨addr A⟩) {6    int value→ 10 = (slot0 + 1) * 10;7    results[slot]→ 10 = value10;8}
  5. workers[i].join();

    19for (int i = 0; i < workerCount; ++i) {20    workers(empty)[i0].join();21}
  6. total ← 0

    23int total→ 0 = 0;24for (int i = 0; i < workerCount; ++i) {
  7. total ← 10

    23int total = 0;24for (int i0 = 0; i < workerCount1; ++i) {25    total→ 10 += results[i]10;26}
  8. std::cout << "workers=" << workerCount << std::endl;

    28    std::cout << "workers=" << workerCount1 << std::endl;29    std::cout << "total=" << total10 << std::endl;30    return 0;31}
    outputworkers=1
    total=10
  1. workerCount ← 3, results ← ⟨addr A⟩, workers ← (empty)

    10int main() {11    int workerCount→ 3 = 3;12    int results→ ⟨addr A⟩[3] = {0, 0, 0};13    std::vector<std::thread> workers→ (empty);
  2. for (int i = 0; i < workerCount; ++i)

    pass 1 of 3
    15for (int i0 = 0; i < workerCount3; ++i) {16    workers(empty).emplace_back(fillSlot1, i0, results⟨addr A⟩);17}
    All 3 passes — pass 1 is the card above
    passi
    10
    21
    32
  3. for (int i = 0; i < workerCount; ++i)

    pass 1 of 3
    19for (int i0 = 0; i < workerCount3; ++i) {20    workers(empty)[i0].join();21}
    All 3 passes — pass 1 is the card above
    passi
    10
    21
    32
  4. value ← 10, results[slot] ← 10

    pass 1 of 3
    5void fillSlot(int slot0, int* results⟨addr A⟩) {6    int value→ 10 = (slot0 + 1) * 10;7    results[slot]→ 10 = value10;8}
    All 3 passes — pass 1 is the card above
    passslotvalueresults[slot]
    10100 10
    21200 20
    32300 30
  5. workers[i].join();

    19for (int i = 0; i < workerCount; ++i) {20    workers(empty)[i0].join();21}
  6. total ← 0

    23int total→ 0 = 0;24for (int i = 0; i < workerCount; ++i) {
  7. total ← 10

    pass 1 of 3
    23int total = 0;24for (int i0 = 0; i < workerCount3; ++i) {25    total→ 10 += results[i]10;26}
    All 3 passes — pass 1 is the card above
    passiresults[i]total
    10100 10
    212010 30
    323030 60
  8. std::cout << "workers=" << workerCount << std::endl;

    28    std::cout << "workers=" << workerCount3 << std::endl;29    std::cout << "total=" << total60 << std::endl;30    return 0;31}
    outputworkers=3
    total=60