2010-02-12 16 views
9

El siguiente código contiene un punto muerto potencial, pero parece ser necesario: para copiar datos a un contenedor de otro, ambos contenedores deben estar bloqueados para evitar que ocurran cambios en otro hilo.Adquirir un bloqueo en dos mutexes y evitar el interbloqueo

void foo::copy(const foo & rhs) 
{ 
    pMutex->lock(); 
    rhs.pMutex->lock(); 
    // do copy 
} 

Foo tiene un contenedor STL y "do copy" consiste esencialmente en usar std :: copy. ¿Cómo bloqueo ambos mutexes sin introducir un punto muerto?

Respuesta

15

Imponga algún tipo de orden total en instancias de foo y adquiera siempre sus bloqueos en orden creciente o decreciente, p., foo1->lock() y luego foo2->lock().

Otro enfoque es usar semántica funcional y, en su lugar, escribir un método foo::clone que crea una nueva instancia en lugar de bloquear una existente.

Si su código está bloqueando mucho, es posible que necesite un complejo algoritmo de prevención de interbloqueo, como el banker's algorithm.

+0

Incluso algo tan simple como las direcciones de este vs RHS trabajaría. siempre bloquee primero el que tiene la dirección más baja. –

+0

clon solo funcionaría bien si no estuviera copiando, y no creo que el uso compartido implícito funcione, pero voy a echar un vistazo. Interesante enfoque Kyle. No puedo ver ningún defecto. – pomeroy

1

¿Qué tal esto?

void foo::copy(const foo & rhs) 
{ 
    scopedLock lock(rhs.pMutex); // release mutex in destructor 
    foo tmp(rhs); 
    swap(tmp); // no throw swap locked internally 
} 

Esta es la excepción segura y bastante segura también. Para guardar el hilo al 100% necesitarás revisar toda la ruta del código y volver a revisarlo con otro par de ojos, después de eso revísalo ...

-1

Para evitar un punto muerto, es probable que sea mejor esperar hasta tanto los recursos pueden ser bloqueados:

no saben qué API está utilizando así que aquí mutex es un código de pseudo arbitraria, asumen que can_lock() sólo comprueba si se puede bloquear un mutex, y que try_lock() devuelve verdadero si lo hizo de bloqueo, y falso , si el mutex ya está bloqueado por otra persona.

void foo::copy(const foo & rhs) 
{ 
    for(;;) 
    { 
     if(! pMutex->cany_lock() || ! rhs.pMutex->cany_lock()) 
     { 
      // Depending on your environment call or dont call sleep() 
      continue; 
     } 
     if(! pMutex->try_lock()) 
      continue; 
     if(! rhs.pMutex->try_lock()) 
     { 
      pMutex->try_lock() 
      continue; 
     } 
     break; 
    } 
    // do copy 
} 
+2

Para evitar un interbloqueo, ¿es mejor introducir un bloqueo en vivo? ¿Y girar, usando 100% de CPU? – bk1e

-1

Usted puede tratar de bloquear tanto las exclusiones mutuas, al mismo tiempo usando scoped_lock o auto_lock .... como transferencia bancaria hacer ...

void Transfer(Receiver recv, Sender send) 
{ 
    scoped_lock rlock(recv.mutex); 
    scoper_lock slock(send.mutex); 

    //do transaction. 
} 
Cuestiones relacionadas