You could do this in three ways I reckon, there are possibly (certainly?) several others:
(a) since the producer thread has to finish before the consumer thread gets executed just eschew all concurrency and call the functions asynchronously in which case your main() would look something like ... :
1 2 3 4 5
|
int main()
{
producer();
consumer();
}
| |
... but this is probably not what you had in mind and/or would like to hear, learn.
So let's look at a couple of other approches that does use concurrency. In both cases the consumer() thread has to wait for the producer thread to complete before it can proceed. The two ways of doing this are:
(b) using an ordinary bool with std::condition_variable: the producer() thread wakes up the consumer() thread through the condition variable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
# include <iostream>
# include <mutex>
# include <future>
# include <condition_variable>
bool boolReady;
std::mutex mutexReady;
std::condition_variable cond_varReady;
void producer()
{
//do whatever consumer() needs as prep
//signal that producer() is done
{
std::lock_guard<std::mutex> lg(mutexReady);
boolReady = true;
}//release lock
cond_varReady.notify_one();//notify_all() to notify multiple waiting threads
}
void consumer()
{
//wait until producer() is done i.e boolReady == true
{
std::unique_lock<std::mutex> ul(mutexReady);
//need unique_lock, lock_guard is not enough because consumer() might lock and unlock the mutex
cond_varReady.wait(ul, [] { return boolReady; });
//to guard against spurious wakeups query the status of boolReady
} // release lock
// do whatever happens after producer() is done
}
int main()
{
auto f1 = std::async(std::launch::async, producer);
auto f2 = std::async(std::launch::async, consumer);
//could also use thread based, instead of task-based, approach as in the OP
}
| |
(c) use an std::atomic<bool> instead of the ordinary bool in (a) in which case no mutex is required. The reasons the mutex is required in (b) are that: [/quote]
(i) In general, reading the writing even for fundmenatal data types is not atomic. Thus you might have a half-written Boolean, ...
(ii) The generated code might change the order of operations, so the providing thread might set the ready flag before the data is provided [/quote] - "The C++ Standard Library" (2nd ed), N Josuttis though the second reason might be addressed by C++17 I think
In any case the mutex solves both problems but might be expensive in terms of resources and it might be worth using atomics instead - the standard guarantees sequential consistency i.e. in a thread atomic operations are guaranteed to happen in the order as programmed. Now the program outline might look as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
# include <iostream>
# include <atomic>
# include <future>
# include <thread>
# include <chrono>
std::atomic<bool> boolReady{false};
//always initialize atomic objects because the default ctor does not fully initialize
void producer()
{
//do whatever consumer() needs as prep
boolReady.store(true);
// store() assigns a new value
}
void consumer()
{
while(!boolReady.load())
//load() returns current value
{
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
// do whatever happens after producer() is done
}
int main()
{
//same as case (b)
}
| |