Multithreading questions

Started by
7 comments, last by ApochPiQ 6 years, 9 months ago

Hello, i have some questions regarding the multithreaded development

1. Lets have 2 threads that are accessing a queue, which is protected by mutex.

Is it necessary to use the same mutex or we can use one mutex for one thread and another mutex for the other thread?

If both threads are trying to access the queue, which is protected by one mutex, what happens? Does one of the threads blocks until the mutex is unlocked or the resource is directly accessed by both threads (which I think leads to data race)? If one of the threads is block, while waiting for the mutex can't we use 2 different mutexes, which would lead to the same result (not very practical, though).

2. We have one thread that is protecting a queue by mutex

What happens if we use second mutex to protect the queue, while the first mutex is already locked? Is this meaningful at all?

Generally my questions is do we always have to use only one mutex to protect single resource or we can use other mutexes?

 

Advertisement

It has to be the same mutex. Otherwise they're not interacting in any way to prevent the other thread from accessing the queue. The point of a mutex is that it is shared, like the thing it is protecting, different threads must acquire it before proceeding to use the protected resource, and only one thread can hold it at once. The other threads will block if they try to access the mutex, until the mutex is freed up by whichever thread acquired it. This means only one thread can access the resource at once - which is the intended behaviour.

There are some esoteric situations where people use multiple mutexes for various functionality but that is not the usual situation.

Note that simultaneously locking multiple mutexes for any reason can lead to deadlock if you are not extremely careful. When it is necessary to lock multiple mutexes for some operation, there is a common pattern called locking order which is employed to defend against deadlocks.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Let's go back one step, why do we have mutexes?

As an example, let's say you have two variables "a", and "b". For some reason (I'll get to that at the end), both a and b must always both be updated, or it crashes. With a single thread, this is trivial. You always first update a and then you update b.

When you have 2 or more threads, this becomes a mess. Several processes all update a and b, each in their own speed, possibly overwriting each other results, and in any case, some of the updates get lost, a and b get out of sync, and stuff crashes.

(I ihope you can see that problem, if not, I'd suggest you look into that first, as it's the core problem of multi-threading.)

 

Ok, so just updating a and b with multiple threads doesn't work, now what? This is where a  mutex comes in. Basically, a single mutex is two guards. One guard is an entry guard named "claim", the other guard is an exit guard named "release". The rule is that all threads first have to pass the entry guard, then they change a and b in the way they want, and then they exit through the exit guard. The guards make sure that only one thread can have entered without having exited. Also, threads may not touch a and b at any other spot then between the entry and the exit guards.

As a thread, it means that if you want to update a and b, you must first pass the entry guard, change a and b, and then pass the exit guard to allow other threads access to the a and b variables. Both guards ensure that at most one thread can change a and b, that is, the small area where you change a and b acts like the single threaded update, in a world surrounded by mullti-threading. See it as a sound-proof room in a busy city. The room has a guard, and allows only one person at a time in. A place for quietness and pondering without getting disturbed by the other parts of the world that surround you. When you're done, you leave the room again for the next person.

Your question now translates to "what would happen if we add more different entry and exit guards?" (or "what happens if we add more doors to the quiet room?"). The short answer is "chaos", since we're back to square one. We started with the observation that having multiple threads updating a and b at the same time will crash. We invented this guard system to ensure only one thread can access a and b. When you add more entry and exit guards, you're back at the situation where several threads can update a and b at the same time, ie you're back at missing updates and getting crashes.

So this is why having a single mutex that is shared between all mutexes is crucial. Even if you have 5 places in the code that update a and b, if all those places use the same entry and exit guard, there is at most 1 place being used at any time, and the thread that is in that place can be sure nobody else is messing around with variables a and b until it leaves through the exit guard.

 

Finally, how does a and b relate to real-world things like queues? If you look closely at a queue, it has two parts. One part is the actually queued message (variable a), and one part is the administration of where to find the entries (variable b). This pretty much holds for anything, even a single integer variable can be seen like that. If you want to add a value to an integer, you first have to read the old value (part a), then compute the sum, and then write the new value (part b). That is, sometimes variables a and b are the same thing.

The latter is however not the best solution. Things like updating a single variable are so common that processors have a few dedicated instructions to do that safely in a multi-threaded environment. It's like a special case entry-exit mutex built into the processor. This only works for simple variables and a few simple operations though, objects and data structures like queues always need mutexes for safe working.

When both threads try to access a queue protected by a shared mutex, they will access it one after the other, in a serial manner. And yes, when one thread acquires the mutex, the other threads will be blocked until they can grab it too.

If your goal is to protect the shared resource (queue in this case), having two mutexes would mean that two threads can access the resource simultaneously. If that's what you want, then cool. But if you want to make the queue thread-safe, sharing a single mutex is the way to go.

40 minutes ago, DiharaW said:

If your goal is to protect the shared resource (queue in this case), having two mutexes would mean that two threads can access the resource simultaneously.

Don't use multiple mutexes to limit the number of simultaneous accessors - use a semaphore.

If you use multiple mutexes, how do you pick which one to wait on?  Then suddenly you have to synchronize that selection process as well or you're in for a world of hurt.

1 hour ago, Nypyren said:

Don't use multiple mutexes to limit the number of simultaneous accessors - use a semaphore.

If you use multiple mutexes, how do you pick which one to wait on?  Then suddenly you have to synchronize that selection process as well or you're in for a world of hurt.

Yeah, i was trying to say that having multiple mutexes is a bad idea. Guess i didn't convey my idea properly. :D  How would a semaphore work to limit the number of simultaneous accessors? I'm curious cause i've only used it for signalling events (i.e kicking off a GPU submit job etc).

Controlling simultaneous accesses is exactly what semaphores are meant for - see Wikipedia for example on how to think of their functionality.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement