views:

544

answers:

3

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.

+3  A: 

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

http://doc.trolltech.com/3.0/threads.html

Stefano Borini
sorry, I did a really bad attempt at pseudocode. Just fixed it. Your answer for the first part was exactly what I was wondering about, thanks!
int3
+1  A: 

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?

yves Baumes
so if I have data I want to lock in specific instances of Foo::f(), should I instantiate the mutex as non-static class data?
int3
Well I guess it should do the trick . THen any call from 'foo' instance from any thread in my exemple will prevent concurrent access. (But not from 2 different instance of course)
yves Baumes
+7  A: 

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 it's ml object until the first thread left the function and the new thread can get the lock on mutex.

sth
+1 using QT api
yves Baumes