|
Boost.Threadssemaphore |
Introduction
Header
Synopsis
Members
Example
The semaphore class defines a classic synchronization primitive invented by the Dutch computer scientist Edsger W. Dijkstra. A semaphore manages an internal counter. This counter never goes below zero, or above a specified maximum value. When calling semaphore::down the calling thread will block until the value is non-zero and then decrement the value in a single atomic operation. When calling semaphore::up the calling thread will increment the value in a single atomic operation, failing if the value has already reached the specified maximum.
Rationale: The semaphore is the simplest synchronization primitive available and is generally the primitive used to build other synchronization concepts at some level of implementation. For this reason Boost.Threads defines the semaphore type in the classic form. This simplifies usage and implementation, but it means that the interface is not as safe as other Boost.Threads interfaces.
Danger: Unlike the mutex models supplied by Boost.Threads, there is no lock_concept for the semaphore to help ensure proper usage. Great care must be taken when using a semaphore object to ensure deadlock or race conditions do not occur.
The dangers are spelled out by [Andrews-83] (function names updated, see historical note below):
Although semaphores can be used to program almost any kind of synchronization, down() and up() are rather unstructured primitives, and so it is easy to err when using them. Execution of each critical section must begin with a down() and end with a up() (on the same semaphore). Omitting a down() or up(), or accidentally coding a down() on one semaphore and a up() on another can have disastrous effects, since mutually exclusive execution would no longer be ensured. Also, when using semaphores, a programmer can forget to include in critical sections all statements that reference shared objects. This, too, could destroy the mutual exclusion required within critical sections. A second difficulty with using semaphores is that both condition synchronization and mutual exclusion are programmed using the same pair of primitives. This makes it difficult to identify the purpose of a given down() or up() operation without looking at the other operations on the corresponding semaphore. Since mutual exclusion and condition synchronization are distinct concepts, they should have distinct notations.
Historical note: Dijkstra's original name for down() was P (short for the Dutch "passeren", "to pass"), and for up() was V (short for the Dutch "vrygeven", "to release").
#include <boost/thread/semaphore.hpp>
namespace boost { class semaphore : private boost::noncopyable // Exposition only. // Class semaphore meets the NonCopyable requirement. { public: explicit semaphore(unsigned count=0, unsigned max=0); ~semaphore(); bool up(unsigned count=1, unsigned* prev=0); void down(); bool down(const xtime& xt); private: unsigned m_count; exposition only [ISO 17.3.2.3/2] unsigned m_max; exposition only [ISO 17.3.2.3/2] }; }
explicit semaphore(unsigned count=0, unsigned max=0);
Effects: As if:
m_count = count;
m_max = (max == 0 ? std::numeric_limits<unsigned>::max()
? max );
~semaphore();
Effects: Destroys *this
.
bool up(unsigned count=1, unsigned* prev=0);
Effects: As if:
unsigned ct;
bool ret;
{ // as a single atomic operation:
ct = m_count;
if (m_count == m_max) ret =
false;
else
{
ret =
true;
++m_count;
}
}
if (prev) *prev = m_count;
return ret;
void down();
Effects: If m_count == 0
, places the current thread in
the blocked state until m_count != 0
.
Finally, --m_count
.
bool down(const xtime& xt);
Effects: If m_count == 0
, places the current thread in
the blocked state until m_count != 0
or xt
is reached. Finally, --m_count
.
Returns: If xt was reached, true, else false.
#include <boost/thread/semaphore.hpp> #include <boost/thread/thread.hpp> #include <iostream> int global_data = 0; boost::semaphore global_semaphore(1); void change_global_data(void*) { global_semaphore.down(); ++global_data; std::cout << "global_data == " << global_data << std::endl; global_semaphore.up(); } int main(int, char*[]) { const int num_threads = 4; boost::thread_group thrds; for (int i=0; i < num_threads; ++i) thrds.create_thread(&change_global_data, 0); thrds.join_all(); return 0; }
The output is:
global_data == 1 global_data == 2 global_data == 3 global_data == 4
Revised 01 October, 2001
© Copyright William E. Kempf 2001 all rights reserved.