Use an atomic counter when threads only need unique ticket numbers.

atomic `fetch_add` updates the counter as one atomic operation and returns the previous value.
tickets Each worker receives a different ticket without a separate mutex.

Atomic Ticket Counter

start
atomic_ticket_counter.cpp
Replay: real traced execution (multi-file project)
#include <atomic>
#include <iostream>
#include <thread>

void takeTicket(std::atomic<int>* nextTicket, int* ticket) {
    *ticket = nextTicket->fetch_add(1);
}

int main() {
    int start = 10;
    std::atomic<int> nextTicket(start);
    int firstTicket = 0;
    int secondTicket = 0;

    std::thread first(takeTicket, &nextTicket, &firstTicket);
    std::thread second(takeTicket, &nextTicket, &secondTicket);

    first.join();
    second.join();

    int issued = nextTicket.load() - start;
    std::cout << "start=" << start << std::endl;
    std::cout << "issued=" << issued << std::endl;
    return 0;
}
#include <atomic>
#include <iostream>
#include <thread>

void takeTicket(std::atomic<int>* nextTicket, int* ticket) {
    *ticket = nextTicket->fetch_add(1);
}

int main() {
    int start = 1;
    std::atomic<int> nextTicket(start);
    int firstTicket = 0;
    int secondTicket = 0;

    std::thread first(takeTicket, &nextTicket, &firstTicket);
    std::thread second(takeTicket, &nextTicket, &secondTicket);

    first.join();
    second.join();

    int issued = nextTicket.load() - start;
    std::cout << "start=" << start << std::endl;
    std::cout << "issued=" << issued << std::endl;
    return 0;
}
#include <atomic>
#include <iostream>
#include <thread>

void takeTicket(std::atomic<int>* nextTicket, int* ticket) {
    *ticket = nextTicket->fetch_add(1);
}

int main() {
    int start = 20;
    std::atomic<int> nextTicket(start);
    int firstTicket = 0;
    int secondTicket = 0;

    std::thread first(takeTicket, &nextTicket, &firstTicket);
    std::thread second(takeTicket, &nextTicket, &secondTicket);

    first.join();
    second.join();

    int issued = nextTicket.load() - start;
    std::cout << "start=" << start << std::endl;
    std::cout << "issued=" << issued << std::endl;
    return 0;
}
  1. start ← 10, nextTicket ← 10, firstTicket ← 0, secondTicket ← 0

    9int main() {10    int start→ 10 = 10; //@start=1, 2011    std::atomic<int> nextTicket→ 10(start10);12    int firstTicket→ 0 = 0;13    int secondTicket→ 0 = 0;1415    std::thread first→ (empty)(takeTicket1, &nextTicket, &firstTicket);16    std::thread second→ (empty)(takeTicket1, &nextTicket, &secondTicket);1718    first(empty).join();19    second.join();
  2. void takeTicket(std::atomic<int>* nextTicket, int* ticket)

    pass 1 of 2
    5void takeTicket(std::atomic<int>* nextTicket⟨addr A⟩, int* ticket⟨addr B⟩) {6    *ticket⟨addr B⟩ = nextTicket⟨addr A⟩->fetch_add(1);7}
  3. void takeTicket(std::atomic<int>* nextTicket, int* ticket)

    pass 2 of 2
    5void takeTicket(std::atomic<int>* nextTicket⟨addr A⟩, int* ticket⟨addr C⟩) {6    *ticket⟨addr C⟩ = nextTicket⟨addr A⟩->fetch_add(1);7}
  4. issued ← 2

    18    first(empty).join();19    second(empty).join();2021    int issued→ 2 = nextTicket12.load() - start10;22    std::cout << "start=" << start10 << std::endl;23    std::cout << "issued=" << issued2 << std::endl;24    return 0;25}
    outputstart=10
    issued=2
  1. start ← 1, nextTicket ← 1, firstTicket ← 0, secondTicket ← 0, first ← (empty)

    9int main() {10    int start→ 1 = 1;11    std::atomic<int> nextTicket→ 1(start1);12    int firstTicket→ 0 = 0;13    int secondTicket→ 0 = 0;1415    std::thread first→ (empty)(takeTicket1, &nextTicket, &firstTicket);16    std::thread second(takeTicket1, &nextTicket, &secondTicket);
  2. void takeTicket(std::atomic<int>* nextTicket, int* ticket)

    pass 1 of 2
    5void takeTicket(std::atomic<int>* nextTicket⟨addr A⟩, int* ticket⟨addr B⟩) {6    *ticket⟨addr B⟩ = nextTicket⟨addr A⟩->fetch_add(1);7}
  3. void takeTicket(std::atomic<int>* nextTicket, int* ticket)

    pass 2 of 2
    5void takeTicket(std::atomic<int>* nextTicket⟨addr A⟩, int* ticket⟨addr C⟩) {6    *ticket⟨addr C⟩ = nextTicket⟨addr A⟩->fetch_add(1);7}
  4. second ← (empty), issued ← 2

    15    std::thread first(takeTicket, &nextTicket, &firstTicket);16    std::thread second→ (empty)(takeTicket1, &nextTicket, &secondTicket);1718    first(empty).join();19    second(empty).join();2021    int issued→ 2 = nextTicket3.load() - start1;22    std::cout << "start=" << start1 << std::endl;23    std::cout << "issued=" << issued2 << std::endl;24    return 0;25}
    outputstart=1
    issued=2
  1. start ← 20, nextTicket ← 20, firstTicket ← 0, secondTicket ← 0

    9int main() {10    int start→ 20 = 20;11    std::atomic<int> nextTicket→ 20(start20);12    int firstTicket→ 0 = 0;13    int secondTicket→ 0 = 0;1415    std::thread first→ (empty)(takeTicket1, &nextTicket, &firstTicket);16    std::thread second(takeTicket1, &nextTicket, &secondTicket);
  2. void takeTicket(std::atomic<int>* nextTicket, int* ticket)

    pass 1 of 2
    5void takeTicket(std::atomic<int>* nextTicket⟨addr A⟩, int* ticket⟨addr B⟩) {6    *ticket⟨addr B⟩ = nextTicket⟨addr A⟩->fetch_add(1);7}
  3. second ← (empty)

    15std::thread first(takeTicket, &nextTicket, &firstTicket);16std::thread second→ (empty)(takeTicket1, &nextTicket, &secondTicket);1718first(empty).join();19second(empty).join();
  4. void takeTicket(std::atomic<int>* nextTicket, int* ticket)

    pass 2 of 2
    5void takeTicket(std::atomic<int>* nextTicket⟨addr A⟩, int* ticket⟨addr C⟩) {6    *ticket⟨addr C⟩ = nextTicket⟨addr A⟩->fetch_add(1);7}
  5. issued ← 2

    18    first.join();19    second(empty).join();2021    int issued→ 2 = nextTicket22.load() - start20;22    std::cout << "start=" << start20 << std::endl;23    std::cout << "issued=" << issued2 << std::endl;24    return 0;25}
    outputstart=20
    issued=2