Mutex Example/Tutorial

Mutex example / tutorial?

Here goes my humble attempt to explain the concept to newbies around the world: (a color coded version on my blog too)

A lot of people run to a lone phone booth (they don't have mobile phones) to talk to their loved ones. The first person to catch the door-handle of the booth, is the one who is allowed to use the phone. He has to keep holding on to the handle of the door as long as he uses the phone, otherwise someone else will catch hold of the handle, throw him out and talk to his wife :) There's no queue system as such. When the person finishes his call, comes out of the booth and leaves the door handle, the next person to get hold of the door handle will be allowed to use the phone.

A thread is : Each person

The mutex is : The door handle

The lock is : The person's hand

The resource is : The phone

Any thread which has to execute some lines of code which should not be modified by other threads at the same time (using the phone to talk to his wife), has to first acquire a lock on a mutex (clutching the door handle of the booth). Only then will a thread be able to run those lines of code (making the phone call).

Once the thread has executed that code, it should release the lock on the mutex so that another thread can acquire a lock on the mutex (other people being able to access the phone booth).

[The concept of having a mutex is a bit absurd when considering real-world exclusive access, but in the programming world I guess there was no other way to let the other threads 'see' that a thread was already executing some lines of code. There are concepts of recursive mutexes etc, but this example was only meant to show you the basic concept. Hope the example gives you a clear picture of the concept.]

With C++11 threading:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m;//you can use std::lock_guard if you want to be exception safe
int i = 0;

void makeACallFromPhoneBooth()
{
m.lock();//man gets a hold of the phone booth door and locks it. The other men wait outside
//man happily talks to his wife from now....
std::cout << i << " Hello Wife" << std::endl;
i++;//no other thread can access variable i until m.unlock() is called
//...until now, with no interruption from other men
m.unlock();//man lets go of the door handle and unlocks the door
}

int main()
{
//This is the main crowd of people uninterested in making a phone call

//man1 leaves the crowd to go to the phone booth
std::thread man1(makeACallFromPhoneBooth);
//Although man2 appears to start second, there's a good chance he might
//reach the phone booth before man1
std::thread man2(makeACallFromPhoneBooth);
//And hey, man3 also joined the race to the booth
std::thread man3(makeACallFromPhoneBooth);

man1.join();//man1 finished his phone call and joins the crowd
man2.join();//man2 finished his phone call and joins the crowd
man3.join();//man3 finished his phone call and joins the crowd
return 0;
}

Compile and run using g++ -std=c++0x -pthread -o thread thread.cpp;./thread

Instead of explicitly using lock and unlock, you can use brackets as shown here, if you are using a scoped lock for the advantage it provides. Scoped locks have a slight performance overhead though.

c++ understanding a lock_guard and mutex in multithreading

Forget about std::lock_guard for a while. It's just convenience (a very useful one, but still just convenience). The synchronisation primitive is the mutex itself.

Mutex is an abbreviation of MUTual EXclusion. It's a synchronisation primitive which allows for one thread to exclude other threads' access to whatever is protected by a mutex. It's usually shared data, but it can be anything (a piece of code, for example).

In your case, you have data which is shared between two threads. To prevent potentially disastrous concurrent access, all accesses to that data must be protected by something. A mutex is a sensible thing to use for this.

So you conceptually bundle your data with a mutex, and whenever any code wants to access (read, modify, write, delete, ...) the data, it must lock the mutex first. Since no more that one thread can ever have a mutex locked at any one time, the data access will be synchronised properly and no race conditions can occur.

With the above, all code accessing the data would look like this:

mymutex.lock();
/* do whatever necessary with the shared data */
mymutex.unlock();

That is fine, as long as

  1. you never forget to correctly match lock and unlock calls, even in the presence of multiple return paths, and
  2. the operations done while the mutex is locked do not throw exceptions

Since the above points are difficult to get right manually (they're a big maintenance burden), there's a way to automate them. That is the std::lock_guard convenience we put aside at start. It's just a simple RAII class which calls lock() on the mutex in its constructor and unlock() in its destructor. With a lock guard, the code for accessing shared data will look like this:

{
std::lock_guard<std::mutex> g(mymutex);
/* do whatever necessary with the shared data */
}

This guarantees that the mutex will correctly be unlocked when the operation finishes, whether by one of potentially many return (or other jump) statements, or by an exception.

Can someone Explain Mutex and how it is used?

A mutex provides mutually exclusive access to a resource; in your case, a database. There aren't multiple threads in your program, but you can have multiple instances of your program running, which is what your mutex is protecting against. Effectively, it is still protecting against access from more than one thread, it's just that those threads can be in separate processes.

Your code is creating a named mutex that can be shared across multiple instances of your application. This is a form of interprocess communication. MSDN documentation on CreateMutex has additional helpful information about named mutexes:

Two or more processes can call
CreateMutex to create the same named
mutex. The first process actually
creates the mutex, and subsequent
processes with sufficient access
rights simply open a handle to the
existing mutex...

Multiple processes can have handles of
the same mutex object, enabling use of
the object for interprocess
synchronization.

A mutex is only necessary here if the database you're working against doesn't inherently support multithreaded access.

Mutex lock on separate classes

What is the correct use of a Mutex?

To synchronize access to a shared resource.

How do I know what it is protecting?

By reading the code. Whatever is accessed when the lock is acquired is what is being protected.

Does it stop all threads from running for the locked period?

No. Only people who wait on the Mutex are "stopped". A thread will wait when you call Lock() if some other thread already has the lock. It will wait until whoever has the lock calls Unlock(). If nobody has the lock then the thread calling Lock() acquires it and does not wait. Multiple threads may be waiting if you have a lock and who gets it when it is unlocked is not necessarily defined. Refer to the documentation for your particular framework.

How do I prevent this? Can I just create a Mutex in reset function and lock it before deleting and unlock it after?

You can prevent simultaneous access to a shared resource by multiple threads if you acquire and release the lock around access to that shared resource. So, yes, you can lock and unlock around your reset code, but you need to do it in all the places that access your shared resource.

In this case objects is your shared resource. If you acquire the lock in the place you delete the contents of objects, you must also acquire the lock in places where you're using it. Since you give out a reference via GetObjects() you would also need to have callers of that realize access needs to be synchronized via this mutex.

An alternative scheme would be to get rid of GetObjects() and have other methods, for example Object *get(int index), that get / set / manipulate the data inside the lock and never actually give out a raw reference to objects.

Does this let the other class know these resources are locked? I am not sure how the mutex knows what information to protect.

No. The Mutex doesn't know anything. You must impose the proper semantics on the code, either by documenting it for callers or thunking all access to the shared resource through an object that knows to acquire the Mutex.

std::lock_guard example, explanation on why it works

myMutex is global, which is what is used to protect myList. guard(myMutex) simply engages the lock and the exit from the block causes its destruction, dis-engaging the lock. guard is just a convenient way to engage and dis-engage the lock.

With that out of the way, mutex does not protect any data. It just provides a way to protect data. It is the design pattern that protects data. So if I write my own function to modify the list as below, the mutex cannot protect it.

void addToListUnsafe(int max, int interval)
{
for (int i = 0; i < max; i++) {
if( (i % interval) == 0) myList.push_back(i);
}
}

The lock only works if all pieces of code that need to access the data engage the lock before accessing and disengage after they are done. This design-pattern of engaging and dis-engaging the lock before and after every access is what protects the data (myList in your case)

Now you would wonder, why use mutex at all, and why not, say, a bool. And yes you can, but you will have to make sure that the bool variable will exhibit certain characteristics including but not limited to the below list.

  1. Not be cached (volatile) across multiple threads.
  2. Read and write will be atomic operation.
  3. Your lock can handle situation where there are multiple execution pipelines (logical cores, etc).

There are different synchronization mechanisms that provide "better locking" (across processes versus across threads, multiple processor versus, single processor, etc) at a cost of "slower performance", so you should always choose a locking mechanism which is just about enough for your situation.

How does a mutex lock and unlock functions prevents CPU reordering?

The short answer is that the body of the pthread_mutex_lock and pthread_mutex_unlock calls will include the necessary platform-specific memory barriers which will prevent the CPU from moving memory accesses within the critical section outside of it. The instruction flow will move from the calling code into the lock and unlock functions via a call instruction, and it is this dynamic instruction trace you have to consider for the purposes of reordering - not the static sequence you see in an assembly listing.

On x86 specifically, you probably won't find explicit, standalone memory barriers inside those methods, since you'll already have lock-prefixed instructions in order to perform the actual locking and unlocking atomically, and these instructions imply a full memory barrier, which prevents the CPU reordering you are concerned about.

For example, on my Ubuntu 16.04 system with glibc 2.23, pthread_mutex_lock is implemented using a lock cmpxchg (compare-and-exchange) and pthread_mutex_unlock is implemented using lock dec (decrement), both of which have full barrier semantics.



Related Topics



Leave a reply



Submit