Professional C__ - Marc Gregoire [398]
packaged_task auto fut = task.get_future(); // Get the future task(2, 3); // Launch the task int res = fut.get(); // Retrieve the result Code snippet from future\packaged_task.cpp This code is just for demonstration purposes. Currently, it launches a separate thread and then calls get() which blocks until the result is calculated. This sounds like a very expensive function call. In real-world applications you can use the promise/future model by periodically checking if there is a result available in the future (using wait_for() as discussed earlier). When the result is not yet available, you can do something else in the mean time, instead of blocking. If you want to give the C++ runtime more control over whether or not a thread is created to calculate something, you can use std::async(). It accepts a function to be executed and returns a future that you can use to retrieve the result. There are two ways in which async() can call your function: Creating a new thread to run your function asynchronously Running your function at the time you call get() on the returned future If you call async() without additional arguments, the run-time library will automatically choose one of the two methods depending on factors like the number of processors in your system. You can force the runtime to use one or the other method by specifying a launch::async or launch::deferred policy argument, respectively. The following example demonstrates the use of async(): int calculate() { return 123; } int main() { auto fut = async(calculate); //auto fut = async(launch::async, calculate); //auto fut = async(launch::deferred, calculate); // Do some more work... // Get result int res = fut.get(); cout << res << endl; return 0; } Code snippet from future\async.cpp As you can see in this example, std::async() is one of the easiest methods to perform some calculations in another thread or the same thread, and retrieve the result afterwards. EXAMPLE: MULTITHREADED LOGGER CLASS It is obvious that you will have to protect access to the queue with a mutex to prevent multiple threads from reading/writing to the queue at the same time. Based on that, you might define the Logger class as follows: class Logger { public: // Starts a background thread writing log entries to a file. Logger(); // Add log entry to the queue. void log(const std::string& entry); protected: void processEntries(); // Mutex and condition variable to protect access to the queue. std::mutex mMutex; std::condition_variable mCondVar; std::queue // The background thread. std::thread mThread; private: // Prevent copy construction and assignment. Logger(const Logger& src); Logger& operator=(const Logger& rhs); }; Code snippet from logger\Version1\Logger.h The implementation is as follows. Note that this initial design has a couple of problems and when you try to run it, it might behave strangely or even crash, as discussed and solved after the example. The inner while loop in the processEntries() method is also worth looking at. It processes all messages in the queue one at a time, and
This section demonstrates how to use threads, the mutex and lock classes, and condition variables to write a multithreaded Logger class. The class allows log messages to be added to a queue from different threads. The Logger class itself will process this queue in another background thread that serially writes the log messages to a file. The class will be designed in three iterations.