Online Book Reader

Home Category

Professional C__ - Marc Gregoire [397]

By Root 1104 0

You need to make sure only one thread is modifying this queue at any given time. You can do this with a mutex:

std::mutex mMutex;

To be able to notify a background thread when an item is added, you need a condition variable:

std::condition_variable mCondVar;

A thread that wants to add an item to the queue first acquires a lock on the mutex, adds the item to the queue, and notifies the background thread:

// Lock mutex and add entry to the queue.

unique_lock lock(mMutex);

mQueue.push(entry);

// Notify condition variable to wake up thread.

mCondVar.notify_all();

The background thread waits for notifications in an infinite loop as follows:

unique_lock lock(mMutex);

while (true) {

// Wait for a notification.

mCondVar.wait(lock);

// Condition variable is notified, so something is in the queue.

// Process queue item...

}

The section “Example: Multithreaded Logger Class” toward the end of this chapter provides a complete example of how to use condition variables to send notifications to other threads.

The standard also defines a helper function called std::notify_all_at_thread_exit(cond, lk) where cond is a condition variable and lk is a unique_lock instance. A thread calling this function should already have acquired the lock lk. When the thread exits, it will automatically execute the following:

lk.unlock();

cond.notify_all();

The lock lk stays locked until the thread exits. So, you need to make sure that this does not cause any deadlocks in your code, for example due to wrong lock ordering. Lock ordering is discussed earlier in this chapter.

FUTURES


As discussed earlier in this chapter, using std::thread to launch a thread that calculates a single result does not make it easy to get the computed result back once the thread has finished executing. Another problem with std::thread is handling errors like exceptions. If a thread throws an exception and this exception is not handled by the thread itself, the C++ runtime will call std::terminate, which usually will terminate the whole application. You can avoid this by using std::future, which is able to transport an uncaught exception to another thread, which can then handle the exception however it wants. Of course, you should always try to handle exceptions in the threads themselves as much as possible, preventing them from leaving the thread.

std::future and std::promise work together to make it easier to retrieve a result from a function that ran in the same thread or in another thread. Once a function, running in the same thread or in another thread, has calculated the value that it wants to return, it puts this value in a promise. This value can then be retrieved through a future. You can think of a future/promise pair as an inter-thread communication channel for a result.

A thread that launched another thread to calculate a value can get this value as follows. T is the type of the calculated result:

future fut = ...; // Is discussed later

T res = fut.get();

The call to get() will retrieve the result and store it in the variable res. If the other thread has not yet finished calculating the result, the call to get() will block until the value becomes available. You can avoid blocking by first asking the future if there is a result available:

if (fut.wait_for(0)) { // Value is available

T res = fut.get();

} else { // Value is not yet available

...

}

A promise is the input side for the result; future is the output side. A promise is something where a thread will store its calculated result. The following code demonstrates how a thread might do this:

promise prom = ...; // Is discussed later

T val = ...; // Calculate result value

prom.set_value(val);

If a thread encounters some kind of error during its calculation, it could store an exception in the promise instead of the value:

prom.set_exception(runtime_error("message"));

A thread that launches another thread to calculate something should give the promise to the newly launched thread, so that it can store the result in it. This is made easy by using std::packaged_task,

Return Main Page Previous Page Next Page

®Online Book Reader