Pregunta

Si se define un mutex dentro de una función, ¿se aplica su bloqueo a las funciones llamadas desde esa función? es decir

void f () {  
    Mutex mutex;  
    g();  
}

¿El bloqueo todavía se aplica a cualquier modificación de datos en g ()?

Además, ¿tengo razón al decir que un bloqueo definido en un método de clase solo se aplicará a instancias específicas de esa clase? Significado:

Class Foo;  
Foo foo1, foo2;
(In thread 1) foo1.bar();  
(In thread 2) foo2.bar();  

¿Cada llamada podría ocurrir simultáneamente?

Sería una buena ventaja si alguien pudiera explicar / señalar enlaces que explican el mecanismo detrás de los mutexes. ¡Gracias! Actualmente estoy trabajando con la biblioteca Qt Thread, si esa información me ayuda.

¿Fue útil?

Solución

En su ejemplo, en realidad no bloquea el mutex, por lo que no impedirá que diferentes hilos accedan a la función al mismo tiempo. También declara el mutex localmente dentro de la función, de modo que cada llamada de función use un objeto mutex local diferente. Incluso si este mutex se bloqueara, cada llamada a la función bloquearía un objeto mutex diferente, sin impedir el acceso simultáneo.

Una mejor estrategia sería una configuración como esta:

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.
  }

  // ...
};

En este caso, mutex está bloqueado mientras exista ml , así también durante el tiempo que el hilo pasa dentro de g () . Si otro hilo llamara a f () durante este tiempo, bloquearía la creación de su objeto ml hasta que el primer hilo dejara la función y el nuevo hilo pueda obtener el bloqueo en mutex .

Otros consejos

Un mutex es algo que agarras, y detendrá cualquier otro hilo que intente agarrarlo hasta que lo sueltes del hilo de agarre.

En su pregunta, tiene una función para asignar una instancia de Mutex. Eso no es suficiente para bloquearlo. Debe llamar específicamente a mutex.lock () (en Qt, pero también en general, a menos que use pthread, en ese caso use pthread_mutex_lock y diviértase con cosas de bajo nivel que dependen de la plataforma. Qt lo resume muy bien).

aquí hay un ejemplo con Qt

  void MyClass::doStuff( int c )
    {
        mutex.lock();
        a = c;
        b = c * 2;
        mutex.unlock();
    } 

Una vez que obtenga el bloqueo, la llamada a g () se realizará desde el hilo que obtuvo el bloqueo, por lo que estará solo en esa llamada suponiendo que no está llamando a g () de otros hilos de otra parte del código. El bloqueo no significa que detendrá todos los otros hilos. Detendrá los hilos que intenten obtener el mismo bloqueo, hasta que se libere el bloqueo.

Si esa es la única forma en que sus hilos pueden llegar a g (), entonces está sincronizado con ese acceso.

Para la segunda parte de su pregunta, si el mutex es un atributo de instancia, entonces serán dos mutexes diferentes. Deberá declarar e instanciar una instancia de mutex de clase y consultarla para su bloqueo. En ese caso, cualquier intento de llamar a un método en la clase que bloquee el mutex de la clase se sincronizará efectivamente, lo que significa que no habrá dos subprocesos que ejecuten ese método juntos.

Por ejemplo (no tengo Qt, así que no puedo compilar este código, y dejé de codificarlo hace 2 años, por lo que no pudo funcionar)

class Foo {
public:
   void method(void) {
      mutex.lock();
      cout << "method called";
      // long computation
      mutex.unlock();
   }

private:
  QMutex mutex;
};

Ok, en este caso, supongamos que tiene dos hilos, 1 y 2, y dos instancias de la clase Foo, a y b. Suponga que el hilo 1 llama a a.method () y el hilo 2 llama a b.method (). En este caso, los dos mutexes son instancias diferentes, por lo que cada subproceso ejecutará la llamada, de forma independiente, y se ejecutará en paralelo.

Suponga que tiene dos hilos, 1 y 2, y una instancia de la clase Foo que se comparte entre los dos hilos. si el subproceso 1 llama a a.method () y luego el subproceso 2 llama a a.method (), el subproceso 2 se detendrá y esperará hasta que se libere el bloqueo de mutex.

Finalmente,

class Foo {
public:
   void method(void) {
      mutex.lock();
      cout << "method called";
      // long computation
      mutex.unlock();
   }

private:
  static QMutex mutex;
};

QMutex Foo::mutex;

En este caso, el mutex es una variable estática de clase. Solo tiene una instancia de mutex para cada instancia de objeto. Suponga que tiene la misma situación que el primer caso anterior: dos hilos y dos instancias. En este caso, cuando el segundo hilo intente llamar a b.method (), tendrá que esperar a que el primer hilo complete a.method (), ya que el bloqueo ahora es único y compartido entre todas las instancias de su clase.

Para más información, Qt tiene un buen tutorial sobre multihilo

https://doc.qt.io/qt-5/threads.html

Su mutex se instala localmente, en la pila. Por lo tanto, una llamada a f () desde un subproceso bloqueará su instancia propia del mutex. Cualquier otra llamada a f () desde otro hilo se bloqueará. ¡Entonces podría ocurrir una condición de carrera con los datos accedidos desde g ()! Incluso difícil lo llamas en la misma instancia de clase:

MyClass foo;
(In thread 1) foo->f();
(In thread 2) foo->f();

Cómo manejar mejor el bloqueo depende de lo que quieras hacer. De acuerdo con lo que dijo, supongo que una mejor política sería modificar la implementación de g () directamente: debe bloquear un mutex declarado como global, por ejemplo, o como estático en g () para ser compartido entre cualquier llamada a g (). ¿Siempre y cuando entienda que desea bloquear sus datos globalmente?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top