In my last post “Monitor Object” I implemented a thread-safe queue. I made two serious errors. Sorry. Today, I will fix these issues.
First, I want to show you again the erroneous implementation from my last post to understand the context.
The key idea of the example is that the Monitor Object is encapsulated in a class and can, therefore, be reused. The class
Monitor uses a
std::mutex as monitor lock and a
std::condition_variable as monitor condition. The class
Monitor provides the minimal interface that a Monitor Object should support.
ThreadSafeQueue in line (1) extends
std::queue in line (3) with a thread-safe interface.
ThreadSafeQueue derives from the class
Monitor and uses its member functions to support the synchronized member functions
add and get. The member functions
get use the monitor lock to protect the Monitor Object, particularly the non-thread-safe
add notifies the waiting thread when a new item was added to
myQueue. This notification is thread-safe. The member function
get (line (3)) deserves more attention. First, the
wait member function of the underlying condition variable is called. This
wait call needs an additional predicate to protect against spurious and lost wakeups (C++ Core Guidelines: Be Aware of the Traps of Condition Variables). The operations modifying
myQueue (lines 4 and 5) must also be protected because they can interleave with the call
myQueue.push(val) (line 6). The Monitor Object
safeQueue line (7) uses the lambda functions in lines (8) and (9) to add or remove a number from the synchronized
ThreadSafeQueue itself is a class template and can hold values from an arbitrary type. One hundred clients add 100 random numbers between 1 – 6 to
safeQueue (line 7), while hundred clients remove these 100 numbers concurrently from the
safeQueue. The output of the program shows the numbers and the thread ids.
This version corrects the exception problem for
add() you can simply use the monitor object with a
I would probably wrap the notification in a “
SendGuard” that contains a
lock_guard and a reference to the
condition_variable and sends the notification upon destruction:
The move constructor and destructor should still be public and represent the whole interface! This would also make it much easier to use in
Finally, here is the full implementation of Dietmar. The numbers correspond to the numbers in my monitorObjec.cpp example.
As a result of the discussion above, Frank has proposed the following version below, which has a consistent and easy-to-use interface for Monitor.
The implementation of the monitor pattern here is based on the flexibility of
std::unique_lock through its template parameter. All of the std RAII lock guards can be used with any class that has
unlock() methods. The
UnlockAndNotify class implements this interface and notifies its condition variable from within the
unlock() method. On top of that, the
Monitor class provides a reduced public interface to create two different kinds of locks, one with notification and one without, by creating a
std::unique_lock on either the whole
UnlockAndNotify instance or just the contained
On the choice of
std::lock_guard I (Frank) prefer the
unique_lock in the interface. This choice allows the user of the
Monitor class more flexibility. I value this flexibility higher than a possible performance difference to
lock_guard which anyway needs to be measured first. I acknowledge that the given examples don’t make use of this extra flexibility.
Afterward, Dietmar further developed Frank’s idea: here, the protected data is kept in the Monitor, making it harder to access it unprotected.
Once more, thanks a lot to Frank and Dietmar. I didn’t want to prove, with my erroneous implementation of a thread-safe queue in my previous post, that concurrency is hard to get right. I’m particularly annoyed that I don’t put the mutex inside a lock (error 2). I teach this in my C++ classes: NNM (No Naked Mutex).
In my next post, I dive into the future of C++: C++23.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, and Marco Parri Empoli.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
|My special thanks to Embarcadero|
|My special thanks to PVS-Studio|
|My special thanks to Tipi.build|
|My special thanks to Take Up Code|
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,