std::counting_semaphore<LeastMaxValue>::acquire

From cppreference.com
 
 
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
(C++11)
Generic lock management
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
(C++11)
Safe Reclamation
(C++26)
Hazard Pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11)(deprecated in C++20)
(C++11)(deprecated in C++20)
Memory ordering
Free functions for atomic operations
Free functions for atomic flags
 
 
void acquire();
(since C++20)

Atomically decrements the internal counter by 1 if it is greater than 0; otherwise blocks until it is greater than 0 and can successfully decrement the internal counter.

Preconditions

(none)

Parameters

(none)

Exceptions

May throw std::system_error.

Example

The example visualizes a concurrent work of several randomized threads when no more than N (N is the semaphore desired value) of the thread-functions are active, while the other might wait on the semaphore.

#include <array>
#include <chrono>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <new>
#include <random>
#include <semaphore>
#include <thread>
#include <vector>
 
using namespace std::literals;
 
constexpr std::size_t max_threads{10U}; // change and see the effect
constexpr std::ptrdiff_t max_sema_threads{3}; // {1} for binary semaphore
std::counting_semaphore semaphore{max_sema_threads};
constexpr auto time_tick{10ms};
 
unsigned rnd()
{
    static std::uniform_int_distribution<unsigned> distribution{2U, 9U}; // [delays]
    static std::random_device engine;
    static std::mt19937 noise{engine()};
    return distribution(noise);
}
 
class alignas(std::hardware_destructive_interference_size) Guide
{
    inline static std::mutex cout_mutex;
    inline static std::chrono::time_point<std::chrono::high_resolution_clock> started_at;
    unsigned delay{rnd()}, occupy{rnd()}, wait_on_sema{};
 
public:
    static void start_time() { started_at = std::chrono::high_resolution_clock::now(); }
 
    void initial_delay() { std::this_thread::sleep_for(delay * time_tick); }
 
    void occupy_sema()
    {
        wait_on_sema =
            static_cast<unsigned>(std::chrono::duration_cast<std::chrono::milliseconds>(
                std::chrono::high_resolution_clock::now() - started_at -
                delay * time_tick).count() / time_tick.count());
        std::this_thread::sleep_for(occupy * time_tick);
    }
 
    void visualize(unsigned id, unsigned x_scale = 2) const
    {
        auto cout_n = [=](auto str, unsigned n)
        {
            for (n *= x_scale; n-- > 0; std::cout << str)
                ;
        };
        std::lock_guard lk{cout_mutex};
        std::cout << '#' << std::setw(2) << id << ' ';
        cout_n("░", delay);
        cout_n("▒", wait_on_sema);
        cout_n("█", occupy);
        std::cout << '\n';
    }
 
    static void show_info()
    {
        std::cout << "\nThreads: " << max_threads << ", Throughput: " << max_sema_threads
                  << " │ Legend: initial delay ░░ │ wait state ▒▒ │ sema occupation ██ \n"
                  << std::endl;
    }
};
 
std::array<Guide, max_threads> guides;
 
void workerThread(unsigned id)
{
    guides[id].initial_delay(); // emulate some work before sema acquisition
    semaphore.acquire();        // wait until a free sema slot is available
    guides[id].occupy_sema();   // emulate some work while sema is acquired
    semaphore.release();
    guides[id].visualize(id);
}
 
int main()
{
    std::vector<std::jthread> threads;
    threads.reserve(max_threads);
 
    Guide::show_info();
    Guide::start_time();
 
    for (auto id{0U}; id != max_threads; ++id)
        threads.push_back(std::jthread(workerThread, id));
}

Possible output:

Default case: max_threads{10U}, max_sema_threads{3}
 
Threads: 10, Throughput: 3 │ Legend: initial delay ░░ │ wait state ▒▒ │ sema occupation ██
 
# 1 ░░░░██████
# 2 ░░░░████████
# 5 ░░░░░░██████████
# 8 ░░░░░░░░░░░░████████████
# 9 ░░░░░░░░░░░░██████████████
# 7 ░░░░░░░░░░░░▒▒▒▒████████████████
# 4 ░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒████████
# 6 ░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒██████████████████
# 3 ░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████████████
# 0 ░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████
 
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
"Enough for everyone" case (no wait states!): max_threads{10U}, max_sema_threads{10}
 
Threads: 10, Throughput: 10 │ Legend: initial delay ░░ │ wait state ▒▒ │ sema occupation ██
 
# 4 ░░░░██████
# 5 ░░░░░░████
# 3 ░░░░██████████
# 1 ░░░░██████████
# 8 ░░░░░░░░████████████
# 6 ░░░░░░░░░░░░░░░░██████
# 7 ░░░░░░░░░░░░░░░░██████
# 9 ░░░░░░░░░░░░░░░░██████████
# 0 ░░░░░░░░░░░░██████████████████
# 2 ░░░░░░░░░░░░░░░░░░████████████
 
──────────────────────────────────────────────────────────────────────────────────────────────────────────────
Binary semaphore case: max_threads{10U}, max_sema_threads{1}
 
Threads: 10, Throughput: 1 │ Legend: initial delay ░░ │ wait state ▒▒ │ sema occupation ██
 
# 6 ░░░░████
# 5 ░░░░▒▒▒▒████
# 4 ░░░░░░░░░░▒▒██████████
# 7 ░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒████████████████
# 2 ░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒██████
# 3 ░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████████████████
# 0 ░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████████████
# 1 ░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒████████
# 8 ░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒██████
# 9 ░░░░░░░░░░░░░░░░░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒██████████████

See also

increments the internal counter and unblocks acquirers
(public member function)
tries to decrement the internal counter without blocking
(public member function)
tries to decrement the internal counter, blocking for up to a duration time
(public member function)
tries to decrement the internal counter, blocking until a point in time
(public member function)