If a mutex is defined within a function, does its lock apply to functions called from that function? ie
void f () {
Mutex mutex;
g();
}
Does the lock still apply to any data modifications in g()?
Also, am I right to say that a lock defined in a class method will only apply to specific instances of that class? Meaning:
Class Foo;
Foo foo1, foo2;
(In thread 1) foo1.bar();
(In thread 2) foo2.bar();
Would each call be able to occur concurrently?
It would be a nice bonus if someone could explain / point out links that explain the mechanism behind mutexes. Thanks! I am currently working with the Qt Thread library, if that information helps.
Mutexes are used to protect shared resources. If the mutex is already locked by another thread, the thread waits for the mutex to become available. The thread that has locked a mutex becomes its current owner and remains the owner until the same thread has unlocked it.
The following two functions are used to create a lock once a mutex has been initialized: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr): Creates a mutex, referenced by the mutex, with the attributes specified by attr.
In computing, a futex (short for "fast userspace mutex") is a kernel system call that programmers can use to implement basic locking, or as a building block for higher-level locking abstractions such as semaphores and POSIX mutexes or condition variables.
A mutex variable acts like a “lock” protecting access to a shared data resource. The basic concept of a mutex as used in Pthreads is that only one thread can lock (or own) a mutex variable at any given time.
In your example you don't actually lock the mutex, so it will not prevent different threads to access the function at the same time. Also you declare the mutex locally inside the function, so that each function call uses a different local mutex object. Even if this mutex would be locked, each function call would lock a different mutex object, not preventing simultaneous access.
A better strategy would be a setup like this:
class A {
QMutex mutex;
void f() {
QMutexLocker ml(mutex); // Acquire a lock on mutex
g();
// The lock on the mutex will be released when ml is destroyed.
// This happens at the end of this function.
}
// ...
};
In this case mutex
is locked as long as ml
exists, so also during the time the thread spends inside g()
. If another thread would call f()
during this time it would block in the creation of its ml
object until the first thread left the function and the new thread can get the lock on mutex
.
A mutex is something you grab, and will stop any other threads trying to grab it until you release it from the grabbing thread.
In your question, you have a function f allocating a Mutex instance. That is not enough to lock it. You have to specifically call mutex.lock() (in Qt, but also in general, unless you use pthread, in that case use pthread_mutex_lock and have fun with low level, platform dependent stuff. Qt abstracts it very well).
here is an example with Qt
void MyClass::doStuff( int c )
{
mutex.lock();
a = c;
b = c * 2;
mutex.unlock();
}
Once you get the lock, the call to g() will be done from the thread who got the lock, so it will be alone in that call assuming that you are not calling g() from other threads from another part of the code. Locking does not mean that it will stop all the other threads. It will stop threads trying to get the same lock, until the lock is released.
If that is the only way for your threads to reach g(), then you are synchronized on that access.
For the second part of your question, If the mutex is an instance attribute, then they will be two different mutexes. You will have to declare and instantiate a class mutex instance and refer to it foro your locking. In that case, any attempt to call a method in the class that locks the class mutex will be effectively synchronized, meaning that no two threads will execute that method together.
For example (I don't have Qt, so I cannot compile this code, and I stopped coding with it 2 years ago, so it could not work)
class Foo {
public:
void method(void) {
mutex.lock();
cout << "method called";
// long computation
mutex.unlock();
}
private:
QMutex mutex;
};
Ok, in this case, suppose you have two threads, 1 and 2, and two instances of the class Foo, a and b. Suppose that thread 1 calls a.method() and thread 2 calls b.method(). In this case, the two mutexes are different instances, so each thread will execute the call, independently, and run in parallel.
Suppose you have two threads, 1 and 2, and one instance of the class Foo which is shared between the two threads. if thread 1 calls a.method() and then thread 2 calls a.method(), thread 2 will stop and wait until the mutex lock is released.
Finally,
class Foo {
public:
void method(void) {
mutex.lock();
cout << "method called";
// long computation
mutex.unlock();
}
private:
static QMutex mutex;
};
QMutex Foo::mutex;
In this case, the mutex is a class static variable. You have only one instance of the mutex for each object instance. Suppose you had the same situation as the first case above: two threads, and two instances. In this case, when the second thread tries to call b.method() it will have to wait for a.method() to be completed by the first thread, as the lock is now unique and shared among all instances of your class.
For more info, Qt has a nice tutorial on multithreading
https://doc.qt.io/qt-5/threads.html
Your mutex is instatiated locally, on the stack. So a call to f() from one thread will be lock its own instance of the mutex. Any other call to f() from another thread will lock its own. So a race condition could occur with data accessed from g() ! Even tough you call it on the same class instance:
MyClass foo;
(In thread 1) foo->f();
(In thread 2) foo->f();
How to better handle lock depends on what you want to do. According to what you told I guess a better policy would be to modify g() implementation directly: it must lock a mutex declared as global for instance, or as being static in g() to be shared among any call to g(). As long as I understand you want to lock your data globally?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With