0% found this document useful (0 votes)
60 views

COMP6771 Advanced C++ Programming: Week 10 Multithreading - Producer/Consumer Problem

The document discusses solutions to the producer-consumer problem in multithreaded programming using C++ features like mutexes, condition variables, and futures. It begins with recapping mutexes and their use for thread synchronization. It then introduces the producer-consumer problem using the example of a manufacturing plant receiving raw materials from trucks and producing products for trains. It presents an initial solution using mutexes that has issues. It further discusses using condition variables to allow threads to wait until certain conditions are met before proceeding. Finally, it introduces futures as a way to make the code asynchronous and minimize waiting by allowing other work to be done while waiting.

Uploaded by

Leandro Estrada
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
60 views

COMP6771 Advanced C++ Programming: Week 10 Multithreading - Producer/Consumer Problem

The document discusses solutions to the producer-consumer problem in multithreaded programming using C++ features like mutexes, condition variables, and futures. It begins with recapping mutexes and their use for thread synchronization. It then introduces the producer-consumer problem using the example of a manufacturing plant receiving raw materials from trucks and producing products for trains. It presents an initial solution using mutexes that has issues. It further discusses using condition variables to allow threads to wait until certain conditions are met before proceeding. Finally, it introduces futures as a way to make the code asynchronous and minimize waiting by allowing other work to be done while waiting.

Uploaded by

Leandro Estrada
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Recap Condition Variables Futures

.... ...... .....

COMP6771
Advanced C++ Programming

Week 10
Multithreading - Producer/Consumer Problem

2016

www.cse.unsw.edu.au/˜cs6771

1
Recap Condition Variables Futures
.... ...... .....

Recap: C++11 Mutexes

C++11 provides Mutex objects in the <mutex> header file.


General idea:
A thread wants to read/write shared memory tries to lock the
mutex object.
If another thread is currently locking the same mutex the first
thread waits until the thread is unlocked or a timer expires.
When the thread obtains the lock it can safely read/write the
shared memory
When the thread has finished using the shared memory it
releases the lock

2
Recap Condition Variables Futures
.... ...... .....

std::mutex

Non-timed mutex class


Member functions:
lock() Tries to obtain the lock on the mutex and blocks
indefinitely until the lock has been aquired.
try lock() Tries to obtain the lock on the mutex, if the
mutex is already locked will immediately return false, if the
lock is obtained will return true.
unlock() Releases the lock currently held.

3
Recap Condition Variables Futures
.... ...... .....

std::mutex example
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 int main() {
6 int i = 1;
7 const long numIterations = 1000000;
8 std::mutex iMutex;
9 std::thread t1([&] {
10 for (int j = 0; j < numIterations; ++j) {
11 iMutex.lock();
12 i++;
13 iMutex.unlock();
14 }
15 });
16 std::thread t2([&] {
17 for (int j = 0; j < numIterations; ++j) {
18 iMutex.lock();
19 i--;
20 iMutex.unlock();
21 }
22 });
23 t1.join();
24 t2.join();
25 std::cout << i << std::endl;
26 }

4
Recap Condition Variables Futures
.... ...... .....

Lock Guards
RAII wrapper class around a mutex.
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4
5 int main() {
6 int i = 1;
7 const long numIterations = 1000000;
8 std::mutex iMutex;
9 std::thread t1([&] {
10 for (int j = 0; j < numIterations; ++j) {
11 std::lock_guard<std::mutex> guard(iMutex);
12 i++;
13 }
14 });
15 std::thread t2([&] {
16 for (int j = 0; j < numIterations; ++j) {
17 std::lock_guard<std::mutex> guard(iMutex);
18 i--;
19 }
20 });
21 t1.join();
22 t2.join();
23 std::cout << i << std::endl;
24 }

5
Recap Condition Variables Futures
.... ...... .....

Producer-Consumer Problem

Scenario: Consider a manufacturing plant which consumes the


raw materials to produce products
The manufacturing plant has limited storage space for both
raw materials and final products.
Trucks deliver raw materials to the plant, however, they arrive
at random time intervals.
If there is no space for the raw materials the trucks have to
wait for space to become available.
Trains remove the final products from the plant, if there are
no products available they wait for one to be manufactured.

6
Recap Condition Variables Futures
.... ...... .....

Producer-Consumer Problem

1 class ManufacturingPlant {
2 public:
3 ManufacturingPlant() : materialsCount{0} {}
4
5 void receiveMaterials(int i);
6 int produceProduct();
7 private:
8 int materialsCount;
9 std::mutex materialsCountMutex;
10 const int CAPACITY = 100;
11 };

7
Recap Condition Variables Futures
.... ...... .....

Producer-Consumer Problem
1 class Truck {
2 public:
3 Truck(ManufacturingPlant& m) : mp{m} {}
4 void deliverMaterials() {
5 mp.receiveMaterials(10);
6 }
7 private:
8 ManufacturingPlant& mp;
9 };
10
11 class Train {
12 public:
13 Train(ManufacturingPlant& m) : mp{m} {}
14 void getProduct() {
15 mp.produceProduct();
16 }
17 private:
18 ManufacturingPlant& mp;
19 };

8
Recap Condition Variables Futures
.... ...... .....

Producer-Consumer Problem
1 void ManufacturingPlant::receiveMaterials(int i) {
2 std::lock_guard<std::mutex> lg(materialsCountMutex);
3 if (materialsCount + i > CAPACITY) {
4 // TODO: wait for capacity!
5 }
6 materialsCount += i;
7 }
8
9 int ManufacturingPlant::produceProduct() {
10 std::lock_guard<std::mutex> lg(materialsCountMutex);
11 if (materialsCount - 10 > 0) {
12 // TODO: wait for materials to arrive
13 }
14 materialsCount -= 10;
15 return 10;
16 }

Problem: how do we release the lock and wait for either capacity
or materials?

9
Recap Condition Variables Futures
.... ...... .....

Condition Variables
Condition variables allow threads to block, release a mutex,
and wait until data is set by another thread or until a time
period has elapsed.
Explicit inter-thread communication.
When we need to check/wait for a condition to be true, we
call wait() on the condition variable.
If we need to signal (communicate) that some data has been
made available we need to notify one() or notify all()
on the condition variable.
Need to add to the class:
1 // used to signal the arrival of materials
2 std::condition_variable hasMaterials;
3 // used to signal the removal of materials
4 std::condition_variable hasCapacity;

10
Recap Condition Variables Futures
.... ...... .....

Producer-Consumer with Condition Variables


1 void ManufacturingPlant::receiveMaterials(int i) {
2 std::unique_lock<std::mutex> lg(materialsCountMutex);
3 hasCapacity.wait(lg, [this, &i] {
4 if (materialsCount + i > CAPACITY) return false;
5 return true;
6 });
7 materialsCount += i;
8 hasMaterials.notify_one();
9 }
10
11 int ManufacturingPlant::produceProduct() {
12 std::unique_lock<std::mutex> lg(materialsCountMutex);
13 hasMaterials.wait(lg, [this] {
14 if (materialsCount - 10 < 0) return false;
15 return true;
16 });
17 materialsCount -= 10;
18 hasCapacity.notify_one();
19 return 10;
20 }

A further example is here:


11
http://baptiste-wicht.com/posts/2012/04/
Recap Condition Variables Futures
.... ...... .....

Futures

Problem: we’ve looked at lots of solutions to prevent


deadlocks and memory corruption. But we’re still stuck with
busy waiting blocks.
How do we make our code asynchronous and minimise
waiting?
e.g., If our train finds that it has to wait for a product to be
manufactured, could it go and collect another product from a
different manufacturing plant and then return to the first at a
later period?

12
Recap Condition Variables Futures
.... ...... .....

std::async

std::async is used to create a thread and return a std::future


which the result of the thread is stored in (e.g. it can work
entirely entirely without shared memory and mutexes).
std::async can hand over control of when to start a thread
to the runtime system. It may not be called immediately if the
system is busy.
The std::future object can be used to check if the result of
the std::async is available.

13
Recap Condition Variables Futures
.... ...... .....

Example sketch
1 #include <iostream>
2 #include <future>
3 #include <thread>
4 #include <chrono>
5
6 int calculate() {
7 return 123;
8 }
9
10 int main() {
11 std::future<int> fut = std::async(calculate);
12 // note can force to launch a new thread using:
13 // std::future<int> fut = std::async(std::launch::async,calculate);
14
15 bool doOtherWork = true;
16 while (doOtherWork) {
17 // check if the result is available.
18 if (fut.wait_for(std::chrono::seconds(0)) == std::future_status::timeout) {
19 // do other work.. e.g. go to a different factory.
20 } else {
21 // either the result is available or the launch has been deferred
22 doOtherWork = false;
23 }
24 }
25
26 int res = fut.get(); // get the result from the future
27 std::cout << res << std::endl;
28 }

Modified example from: http:


14
Recap Condition Variables Futures
.... ...... .....

Throwing exceptions across threads

Futures can be used to transport exceptions across threads.


When you call get() on the future you may get the result or
the exception thrown.
1 int calculate() {
2 throw std::runtime_error("Exception thrown from thread");
3 }

1 try {
2 int res = fut.get(); // get the result from the future
3 std::cout << res << std::endl;
4 } catch (const std::exception& ex) {
5 std::cout << "Exception caught" << std::endl;
6 }

15
Recap Condition Variables Futures
.... ...... .....

Readings

Chapter 23 Professional C++


http://baptiste-wicht.com/posts/2012/03/
cp11-concurrency-tutorial-part-2-protect-shared-data.
html

16

You might also like