Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/thread/counting semaphore"

From cppreference.com
< cpp‎ | thread
m (changed a thread creation comment since we won't detach it and moved the using nmspc std::literals into the thread proc to make using the seconds literal more obvious)
m (+exposition-only member counter)
 
(12 intermediate revisions by 8 users not shown)
Line 2: Line 2:
 
{{cpp/thread/counting_semaphore/navbar}}
 
{{cpp/thread/counting_semaphore/navbar}}
 
{{dcl begin}}
 
{{dcl begin}}
{{dcl header | semaphore}}
+
{{dcl header|semaphore}}
{{dcl | num=1 | since=c++20 | 1=
+
{{dcl|num=1|since=c++20|1=
template<std::ptrdiff_t LeastMaxValue = /* implementation-defined */>
+
template< std::ptrdiff_t LeastMaxValue = /* implementation-defined */ >
 
class counting_semaphore;
 
class counting_semaphore;
 
}}
 
}}
{{dcl | num=2 | since=c++20 | 1=
+
{{dcl|num=2|since=c++20|1=
 
using binary_semaphore = std::counting_semaphore<1>;
 
using binary_semaphore = std::counting_semaphore<1>;
 
}}
 
}}
Line 13: Line 13:
  
 
@1@ A {{tt|counting_semaphore}} is a lightweight synchronization primitive that can control access to a shared resource. Unlike a {{lc|std::mutex}}, a {{tt|counting_semaphore}} allows more than one concurrent access to the same resource, for at least {{tt|LeastMaxValue}} concurrent accessors. The program is ill-formed if {{tt|LeastMaxValue}} is negative.
 
@1@ A {{tt|counting_semaphore}} is a lightweight synchronization primitive that can control access to a shared resource. Unlike a {{lc|std::mutex}}, a {{tt|counting_semaphore}} allows more than one concurrent access to the same resource, for at least {{tt|LeastMaxValue}} concurrent accessors. The program is ill-formed if {{tt|LeastMaxValue}} is negative.
@2@ {{tt|binary_semaphore}} is an alias for specialization of {{lc|std::counting_semaphore}} with {{tt|LeastMaxValue}} being {{c|1}}. Implementations may implement {{tt|binary_semaphore}} more efficiently than the default implementation of {{lc|std::counting_semaphore}}.
 
  
A {{tt|counting_semaphore}} contains an internal counter initialized by the constructor. This counter is decremented by calls to {{c|acquire()}} and related methods, and is incremented by calls to {{c|release()}}. When the counter is zero, {{c|acquire()}} blocks until the counter is incremented, but {{c|try_acquire()}} does not block; {{c|try_acquire_for()}} and {{c|try_acquire_until()}} block until the counter is incremented or a timeout is reached.
+
@2@ {{tt|binary_semaphore}} is an alias for specialization of {{tt|std::counting_semaphore}} with {{tt|LeastMaxValue}} being {{c|1}}. Implementations may implement {{tt|binary_semaphore}} more efficiently than the default implementation of {{tt|std::counting_semaphore}}.
  
Similar to {{lc|std::condition_variable}}'s {{c|wait()}}, {{tt|counting_semaphore}}'s {{c|try_acquire()}} can spuriously fail.
+
A {{tt|counting_semaphore}} contains an internal counter initialized by the constructor. This counter is decremented by calls to {{lc|acquire()}} and related methods, and is incremented by calls to {{lc|release()}}. When the counter is zero, {{lc|acquire()}} blocks until the counter is incremented, but {{lc|try_acquire()}} does not block; {{lc|try_acquire_for()}} and {{lc|try_acquire_until()}} block until the counter is incremented or a timeout is reached.
 +
 
 +
Similar to {{lc|std::condition_variable::wait()}}, {{tt|counting_semaphore}}'s {{lc|try_acquire()}} can spuriously fail.
  
 
Specializations of {{tt|std::counting_semaphore}} are not {{named req|DefaultConstructible}}, {{named req|CopyConstructible}}, {{named req|MoveConstructible}}, {{named req|CopyAssignable}}, or {{named req|MoveAssignable}}.
 
Specializations of {{tt|std::counting_semaphore}} are not {{named req|DefaultConstructible}}, {{named req|CopyConstructible}}, {{named req|MoveConstructible}}, {{named req|CopyAssignable}}, or {{named req|MoveAssignable}}.
 +
 +
===Data Members===
 +
{{dsc begin}}
 +
{{dsc hitem|Member name|Definition}}
 +
{{dsc expos mem obj|counter|private=yes|The internal counter of type {{lc|std::ptrdiff_t}}.}}
 +
{{dsc end}}
  
 
===Member functions===
 
===Member functions===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/thread/counting_semaphore/dsc constructor}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc constructor}}
{{dsc inc | cpp/thread/counting_semaphore/dsc destructor}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc destructor}}
{{dsc inc | cpp/thread/counting_semaphore/dsc operator{{=}}}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc operator{{=}}}}
  
{{dsc h2 | Operations}}
+
{{dsc h2|Operations}}
{{dsc inc | cpp/thread/counting_semaphore/dsc release}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc release}}
{{dsc inc | cpp/thread/counting_semaphore/dsc acquire}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc acquire}}
{{dsc inc | cpp/thread/counting_semaphore/dsc try_acquire}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc try_acquire}}
{{dsc inc | cpp/thread/counting_semaphore/dsc try_acquire_for}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc try_acquire_for}}
{{dsc inc | cpp/thread/counting_semaphore/dsc try_acquire_until}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc try_acquire_until}}
  
{{dsc h2 | Constants}}
+
{{dsc h2|Constants}}
{{dsc inc | cpp/thread/counting_semaphore/dsc max}}
+
{{dsc inc|cpp/thread/counting_semaphore/dsc max}}
 
{{dsc end}}
 
{{dsc end}}
  
 
===Notes===
 
===Notes===
As its name indicates, the {{tt|LeastMaxValue}} is the ''minimum'' max value, not the ''actual'' max value. Thus {{c|max()}} can yield a number larger than {{tt|LeastMaxValue}}.
+
As its name indicates, the {{tt|LeastMaxValue}} is the ''minimum'' max value, not the ''actual'' max value. Thus {{lc|max()}} can yield a number larger than {{tt|LeastMaxValue}}.
  
 
Unlike {{lc|std::mutex}} a {{tt|counting_semaphore}} is not tied to threads of execution - acquiring a semaphore can occur on a different thread than releasing the semaphore, for example. All operations on {{tt|counting_semaphore}} can be performed concurrently and without any relation to specific threads of execution, with the exception of the destructor which cannot be performed concurrently but can be performed on a different thread.
 
Unlike {{lc|std::mutex}} a {{tt|counting_semaphore}} is not tied to threads of execution - acquiring a semaphore can occur on a different thread than releasing the semaphore, for example. All operations on {{tt|counting_semaphore}} can be performed concurrently and without any relation to specific threads of execution, with the exception of the destructor which cannot be performed concurrently but can be performed on a different thread.
  
Semaphores are also often used for the semantics of signalling/notifying rather than mutual exclusion, by initializing the semaphore with {{c|0}} and thus blocking the receiver(s) that try to {{c|acquire()}}, until the notifier "signals" by invoking {{c|release(n)}}. In this respect semaphores can be considered alternatives to {{lc|std::condition_variable}}s, often with better performance.
+
Semaphores are also often used for the semantics of signaling/notifying rather than mutual exclusion, by initializing the semaphore with {{c|0}} and thus blocking the receiver(s) that try to {{lc|acquire()}}, until the notifier "signals" by invoking {{c|release(n)}}. In this respect semaphores can be considered alternatives to {{lc|std::condition_variable}}s, often with better performance.
 +
 
 +
{{feature test macro|__cpp_lib_semaphore|{{tt|std::counting_semaphore}}, {{tt|std::binary_semaphore}}|std=C++20|value=201907L}}
  
 
===Example===
 
===Example===
 
{{example
 
{{example
|
+
|
| code=
+
|code=
#include <iostream>
+
#include <thread>
+
 
#include <chrono>
 
#include <chrono>
 +
#include <iostream>
 
#include <semaphore>
 
#include <semaphore>
 +
#include <thread>
  
 
// global binary semaphore instances
 
// global binary semaphore instances
Line 58: Line 67:
 
// objects are in non-signaled state
 
// objects are in non-signaled state
 
std::binary_semaphore
 
std::binary_semaphore
smphSignalMainToThread(0),
+
    smphSignalMainToThread{0},
smphSignalThreadToMain(0);
+
    smphSignalThreadToMain{0};
  
 
void ThreadProc()
 
void ThreadProc()
{
+
{
// wait for a signal from the main proc
+
    // wait for a signal from the main proc
 
     // by attempting to decrement the semaphore
 
     // by attempting to decrement the semaphore
 
     smphSignalMainToThread.acquire();
 
     smphSignalMainToThread.acquire();
Line 75: Line 84:
 
     // being done by the thread
 
     // being done by the thread
 
     using namespace std::literals;
 
     using namespace std::literals;
std::this_thread::sleep_for(3s);
+
    std::this_thread::sleep_for(3s);
  
 
     std::cout << "[thread] Send the signal\n"; // message
 
     std::cout << "[thread] Send the signal\n"; // message
Line 86: Line 95:
 
{
 
{
 
     // create some worker thread
 
     // create some worker thread
     std::jthread thrWorker(ThreadProc);
+
     std::thread thrWorker(ThreadProc);
  
 
     std::cout << "[main] Send the signal\n"; // message
 
     std::cout << "[main] Send the signal\n"; // message
Line 99: Line 108:
  
 
     std::cout << "[main] Got the signal\n"; // response message
 
     std::cout << "[main] Got the signal\n"; // response message
 +
    thrWorker.join();
 
}
 
}
| output=
+
|output=
 
[main] Send the signal
 
[main] Send the signal
 
[thread] Got the signal
 
[thread] Got the signal
Line 106: Line 116:
 
[main] Got the signal
 
[main] Got the signal
 
}}
 
}}
{{langlinks|ja|zh}}
+
 
 +
{{langlinks|es|ja|ru|zh}}

Latest revision as of 12:13, 19 May 2024

 
 
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
counting_semaphorebinary_semaphore
(C++20)(C++20)
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
 
 
Defined in header <semaphore>
template< std::ptrdiff_t LeastMaxValue = /* implementation-defined */ >
class counting_semaphore;
(1) (since C++20)
using binary_semaphore = std::counting_semaphore<1>;
(2) (since C++20)
1) A counting_semaphore is a lightweight synchronization primitive that can control access to a shared resource. Unlike a std::mutex, a counting_semaphore allows more than one concurrent access to the same resource, for at least LeastMaxValue concurrent accessors. The program is ill-formed if LeastMaxValue is negative.
2) binary_semaphore is an alias for specialization of std::counting_semaphore with LeastMaxValue being 1. Implementations may implement binary_semaphore more efficiently than the default implementation of std::counting_semaphore.

A counting_semaphore contains an internal counter initialized by the constructor. This counter is decremented by calls to acquire() and related methods, and is incremented by calls to release(). When the counter is zero, acquire() blocks until the counter is incremented, but try_acquire() does not block; try_acquire_for() and try_acquire_until() block until the counter is incremented or a timeout is reached.

Similar to std::condition_variable::wait(), counting_semaphore's try_acquire() can spuriously fail.

Specializations of std::counting_semaphore are not DefaultConstructible, CopyConstructible, MoveConstructible, CopyAssignable, or MoveAssignable.

Contents

[edit] Data Members

Member name Definition
counter (private) The internal counter of type std::ptrdiff_t.
(exposition-only member object*)

[edit] Member functions

constructs a counting_semaphore
(public member function) [edit]
destructs the counting_semaphore
(public member function) [edit]
operator=
[deleted]
counting_semaphore is not assignable
(public member function) [edit]
Operations
increments the internal counter and unblocks acquirers
(public member function) [edit]
decrements the internal counter or blocks until it can
(public member function) [edit]
tries to decrement the internal counter without blocking
(public member function) [edit]
tries to decrement the internal counter, blocking for up to a duration time
(public member function) [edit]
tries to decrement the internal counter, blocking until a point in time
(public member function) [edit]
Constants
[static]
returns the maximum possible value of the internal counter
(public static member function) [edit]

[edit] Notes

As its name indicates, the LeastMaxValue is the minimum max value, not the actual max value. Thus max() can yield a number larger than LeastMaxValue.

Unlike std::mutex a counting_semaphore is not tied to threads of execution - acquiring a semaphore can occur on a different thread than releasing the semaphore, for example. All operations on counting_semaphore can be performed concurrently and without any relation to specific threads of execution, with the exception of the destructor which cannot be performed concurrently but can be performed on a different thread.

Semaphores are also often used for the semantics of signaling/notifying rather than mutual exclusion, by initializing the semaphore with 0 and thus blocking the receiver(s) that try to acquire(), until the notifier "signals" by invoking release(n). In this respect semaphores can be considered alternatives to std::condition_variables, often with better performance.

Feature-test macro Value Std Feature
__cpp_lib_semaphore 201907L (C++20) std::counting_semaphore, std::binary_semaphore

[edit] Example

#include <chrono>
#include <iostream>
#include <semaphore>
#include <thread>
 
// global binary semaphore instances
// object counts are set to zero
// objects are in non-signaled state
std::binary_semaphore
    smphSignalMainToThread{0},
    smphSignalThreadToMain{0};
 
void ThreadProc()
{
    // wait for a signal from the main proc
    // by attempting to decrement the semaphore
    smphSignalMainToThread.acquire();
 
    // this call blocks until the semaphore's count
    // is increased from the main proc
 
    std::cout << "[thread] Got the signal\n"; // response message
 
    // wait for 3 seconds to imitate some work
    // being done by the thread
    using namespace std::literals;
    std::this_thread::sleep_for(3s);
 
    std::cout << "[thread] Send the signal\n"; // message
 
    // signal the main proc back
    smphSignalThreadToMain.release();
}
 
int main()
{
    // create some worker thread
    std::thread thrWorker(ThreadProc);
 
    std::cout << "[main] Send the signal\n"; // message
 
    // signal the worker thread to start working
    // by increasing the semaphore's count
    smphSignalMainToThread.release();
 
    // wait until the worker thread is done doing the work
    // by attempting to decrement the semaphore's count
    smphSignalThreadToMain.acquire();
 
    std::cout << "[main] Got the signal\n"; // response message
    thrWorker.join();
}

Output:

[main] Send the signal
[thread] Got the signal
[thread] Send the signal
[main] Got the signal