Provide a thread-aware bounded queue of values.
More...
Namespaces |
namespace | bdlcc |
Detailed Description
- Outline
-
-
- Purpose:
- Provide a thread-aware bounded queue of values.
-
- Classes:
-
- See also:
- Component bdlcc_fixedqueue
-
- Description:
- This component defines a type,
bdlcc::BoundedQueue
, that provides an efficient, thread-aware bounded (capacity fixed at construction) queue of values. This class is ideal for synchronization and communication between threads in a producer-consumer model when a bounded queue is appropriate. Under most cicrumstances developers should prefer this component to the older {bdlcc_fixedqueue} (see Comparison to FixedQueue).
- The queue provides
pushBack
and popFront
methods for pushing data into the queue and popping data from the queue. When the queue is full, the pushBack
methods block until data is removed from the queue. When the queue is empty, the popFront
methods block until data appears in the queue. Non-blocking methods tryPushBack
and tryPopFront
are also provided. The tryPushBack
method fails immediately, returning a non-zero value, if the queue is full. The tryPopFront
method fails immediately, returning a non-zero value, if the queue is empty.
- The queue may be placed into a "enqueue disabled" state using the
disablePushBack
method. When disabled, pushBack
and tryPushBack
fail immediately and return an error code. Any threads blocked in pushBack
when the queue is enqueue disabled return from pushBack
immediately and return an error code. The queue may be restored to normal operation with the enablePushBack
method.
- The queue may be placed into a "dequeue disabled" state using the
disablePopFront
method. When dequeue disabled, popFront
and tryPopFront
fail immediately and return an error code. Any threads blocked in popFront
when the queue is dequeue disabled return from popFront
immediately and return an error code. The queue may be restored to normal operation with the enablePopFront
method.
-
- Comparison To FixedQueue:
- Both
bdlcc::FixedQueue
and bdlcc::BoundedQueue
provide thread-aware bounded queues. Under most circumstances developers should prefer {bdlcc_boundedqueue}: it is newer, has additional features, and provides better performance under most circumstances. bdlcc::BoundedQueue
is not quite a drop in replacement for bdlcc::FixedQueue
so both types are currently maintained. There is additional information about performance of various queues in the article Concurrent Queue Evaluation (https://tinyurl.com/mr2un9f7).
-
- Template Requirements:
bdlcc::BoundedQueue
is a template that is parameterized on the type of element contained within the queue. The supplied template argument, TYPE
, must provide both a default constructor and a copy constructor, as well as an assignment operator. If the default constructor accepts a bslma::Allocator *
, TYPE
must declare the uses bslma::Allocator
trait (see bslma_usesbslmaallocator
) so that the allocator of the queue is propagated to the elements contained in the queue.
-
- Exception Safety:
- A
bdlcc::BoundedQueue
is exception neutral, and all of the methods of bdlcc::BoundedQueue
provide the basic exception safety guarantee (see bsldoc_glossary
). If an exception occurs while writing to an element, the element is marked unusable until after a read attempt from the element (at which point the element is "reclaimed"). This failure to write does not increment the result returned by numElements
. Hence, numElements() == capacity()
is not a valid replacement for isFull()
.
-
- Move Semantics in C++03:
- Move-only types are supported by
bdlcc::BoundedQueue
on C++11 platforms only (where BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES
is defined), and are not supported on C++03 platforms. Unfortunately, in C++03, there are user types where a bslmf::MovableRef
will not safely degrade to a lvalue reference when a move constructor is not available (types providing a constructor template taking any type), so bslmf::MovableRefUtil::move
cannot be used directly on a user supplied template type. See internal bug report 99039150 for more information.
-
- Usage:
- This section illustrates intended use of this component.
-
- Example 1: A Simple Thread Pool:
- In the following example a
bdlcc::BoundedQueue
is used to communicate between a single "producer" thread and multiple "consumer" threads. The "producer" will push work requests onto the queue, and each "consumer" will iteratively take a work request from the queue and service the request. This example shows a partial, simplified implementation of the bdlmt::FixedThreadPool
class. See component bdlmt_fixedthreadpool
for more information.
- First, we define a utility classes that handles a simple "work item":
struct my_WorkData {
};
struct my_WorkRequest {
enum RequestType {
e_WORK = 1,
e_STOP = 2
};
RequestType d_type;
my_WorkData d_data;
};
Next, we provide a simple function to service an individual work item. The details are unimportant for this example: void myDoWork(const my_WorkData& data)
{
(void)data;
}
Then, we define a myConsumer
function that will pop elements off the queue and process them. Note that the call to queue->popFront()
will block until there is an element available on the queue. This function will be executed in multiple threads, so that each thread waits in queue->popFront()
, and bdlcc::BoundedQueue
guarantees that each thread gets a unique element from the queue: void myConsumer(bdlcc::BoundedQueue<my_WorkRequest> *queue)
{
while (1) {
my_WorkRequest item;
item.d_type = my_WorkRequest::e_WORK;
assert(0 == queue->popFront(&item));
if (item.d_type == my_WorkRequest::e_STOP) { break; }
myDoWork(item.d_data);
}
}
Finally, we define a myProducer
function that serves multiple roles: it creates the bdlcc::BoundedQueue
, starts the consumer threads, and then produces and enqueues work items. When work requests are exhausted, this function enqueues one e_STOP
item for each consumer queue. This e_STOP
item indicates to the consumer thread to terminate its thread-handling function.
- Note that, although the producer cannot control which thread
pop
s a particular work item, it can rely on the knowledge that each consumer thread will read a single e_STOP
item and then terminate. void myProducer(int numThreads)
{
enum {
k_MAX_QUEUE_LENGTH = 100,
k_NUM_WORK_ITEMS = 1000
};
bdlcc::BoundedQueue<my_WorkRequest> queue(k_MAX_QUEUE_LENGTH);
bslmt::ThreadGroup consumerThreads;
consumerThreads.addThreads(bdlf::BindUtil::bind(&myConsumer, &queue),
numThreads);
for (int i = 0; i < k_NUM_WORK_ITEMS; ++i) {
my_WorkRequest item;
item.d_type = my_WorkRequest::e_WORK;
item.d_data = my_WorkData();
queue.pushBack(item);
}
for (int i = 0; i < numThreads; ++i) {
my_WorkRequest item;
item.d_type = my_WorkRequest::e_STOP;
queue.pushBack(item);
}
consumerThreads.joinAll();
}