Reputation: 6546
Please consider this classical approach, I have simplified it to highlight the exact question:
#include <iostream>
#include <mutex>
using namespace std;
class Test
{
public:
void modify()
{
std::lock_guard<std::mutex> guard(m_);
// modify data
}
private:
/// some private data
std::mutex m_;
};
This is the classical approach of using std::mutex
to avoid data races.
The question is why are we keeping an extra std::mutex
in our class? Why can't we declare it every time before the declaration of std::lock_guard
like this?
void modify()
{
std::mutex m_;
std::lock_guard<std::mutex> guard(m_);
// modify data
}
Upvotes: 7
Views: 1440
Reputation: 3018
You need a mutex at class level. Otherwise, each thread has a mutex for itself, and therefore the mutex has no effect.
If for some reason you don't want your mutex to be stored in a class attribute, you could use a static mutex as shown below.
void modify()
{
static std::mutex myMutex;
std::lock_guard<std::mutex> guard(myMutex);
// modify data
}
Note that here there is only 1 mutex for all the class instances. If the mutex is stored in an attribute, you would have one mutex per class instance. Depending on your needs, you might prefer one solution or the other.
Upvotes: 3
Reputation: 35154
The misunderstanding comes from what the mutex
is and what the lock_guard
is good for.
A mutex is an object that is shared among different threads, and each thread can lock and release the mutex. That's how synchronization among different threads works. So you can work with m_.lock()
and m_.unlock()
as well, yet you have to be very careful that all code paths (including exceptional exits) in your function actually unlocks the mutex.
To avoid the pitfall of missing unlocks, a lock_guard
is a wrapper object which locks the mutex at wrapper object creation and unlocks it at wrapper object destruction. Since the wrapper object is an object with automatic storage duration, you will never miss an unlock - that's why.
A local mutex does not make sense, as it would be local and not a shared ressource. A local lock_guard perfectly makes sense, as the autmoatic storage duration prevents missing locks / unlocks.
Hope it helps.
Upvotes: 7
Reputation: 6310
Synchronization of threads involves checking if there is another thread executing the critical section. A mutex
is the objects that holds the state for us to check if it was "locked" by a thread. lock_guard
on the other hand is a wrapper that lock
s the mutex
on initialization and unlock
s it during destruction.
Having realized that, it should be clearer why there has to be only one instance of the mutex
that all lock_guard
s need access to - they need to check if it's clear to enter the critical section against the same object. In the second snippet of your question each function call creates a separate mutex
that is seen and accessible only in its local context.
Upvotes: 3
Reputation: 26286
This all depends on the context of what you want to prevent from being executed in parallel.
A mutex will work when multiple threads try to access the same mutex object. So when 2 threads try to access and acquire the lock of a mutex object, only one of them will succeed.
Now in your second example, if two threads call modify()
, each thread will have its own instance of that mutex, so nothing will stop them from running that function in parallel as if there's no mutex.
So to answer your question: It depends on the context. The mission of the design is to ensure that all threads that should not be executed in parallel will hit the same mutex object at the critical part.
Upvotes: 4
Reputation: 73443
Lets say two threads are calling modify
in parallel. So each thread gets its own, new mutex. So the guard
has no effect as each guard is locking a different mutex. The resource you are trying to protect from race-conditions will be exposed.
Upvotes: 21