Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::lock_guard example, explanation on why it works

I've reached a point in my project that requires communication between threads on resources that very well may be written to, so synchronization is a must. However I don't really understand synchronization at anything other than the basic level.

Consider the last example in this link: http://www.bogotobogo.com/cplusplus/C11/7_C11_Thread_Sharing_Memory.php

#include <iostream> #include <thread> #include <list> #include <algorithm> #include <mutex>  using namespace std;  // a global variable std::list<int>myList;  // a global instance of std::mutex to protect global variable std::mutex myMutex;  void addToList(int max, int interval) {     // the access to this function is mutually exclusive     std::lock_guard<std::mutex> guard(myMutex);     for (int i = 0; i < max; i++) {         if( (i % interval) == 0) myList.push_back(i);     } }  void printList() {     // the access to this function is mutually exclusive     std::lock_guard<std::mutex> guard(myMutex);     for (auto itr = myList.begin(), end_itr = myList.end(); itr != end_itr; ++itr ) {         cout << *itr << ",";     } }  int main() {     int max = 100;      std::thread t1(addToList, max, 1);     std::thread t2(addToList, max, 10);     std::thread t3(printList);      t1.join();     t2.join();     t3.join();      return 0; } 

The example demonstrates how three threads, two writers and one reader, accesses a common resource(list).

Two global functions are used: one which is used by the two writer threads, and one being used by the reader thread. Both functions use a lock_guard to lock down the same resource, the list.

Now here is what I just can't wrap my head around: The reader uses a lock in a different scope than the two writer threads, yet still locks down the same resource. How can this work? My limited understanding of mutexes lends itself well to the writer function, there you got two threads using the exact same function. I can understand that, a check is made right as you are about to enter the protected area, and if someone else is already inside, you wait.

But when the scope is different? This would indicate that there is some sort of mechanism more powerful than the process itself, some sort of runtime environment blocking execution of the "late" thread. But I thought there were no such things in c++. So I am at a loss.

What exactly goes on under the hood here?

like image 432
Deviatore Avatar asked Feb 07 '16 10:02

Deviatore


People also ask

How does STD lock_guard work?

The point of lock_guard is just to make locking and unlocking the mutex easier for you. For example, if you manually lock / unlock , but your function throws an exception somewhere in the middle, it will never reach the unlock statement.

What is std :: lock_guard?

std::lock_guard The class lock_guard is a mutex wrapper that provides a convenient RAII-style mechanism for owning a mutex for the duration of a scoped block. When a lock_guard object is created, it attempts to take ownership of the mutex it is given.

What is the difference between unique_lock and lock_guard?

A lock_guard always holds a lock from its construction to its destruction. A unique_lock can be created without immediately locking, can unlock at any point in its existence, and can transfer ownership of the lock from one instance to another.

What is the benefit of using std :: unique_lock?

The benefit to using std::unique_lock<> comes from two things: you can transfer ownership of the lock between instances, and. the std::unique_lock<> object does not have to own the lock on the mutex it is associated with.


1 Answers

Let’s have a look at the relevant line:

std::lock_guard<std::mutex> guard(myMutex); 

Notice that the lock_guard references the global mutex myMutex. That is, the same mutex for all three threads. What lock_guard does is essentially this:

  • Upon construction, it locks myMutex and keeps a reference to it.
  • Upon destruction (i.e. when the guard's scope is left), it unlocks myMutex.

The mutex is always the same one, it has nothing to do with the scope. The point of lock_guard is just to make locking and unlocking the mutex easier for you. For example, if you manually lock/unlock, but your function throws an exception somewhere in the middle, it will never reach the unlock statement. So, doing it the manual way you have to make sure that the mutex is always unlocked. On the other hand, the lock_guard object gets destroyed automatically whenever the function is exited – regardless how it is exited.

like image 135
mindriot Avatar answered Sep 20 '22 23:09

mindriot