2009-11-22 23 views
17

Si desbloquea un mutex ya desbloqueado, ¿el comportamiento es inseguro, seguro o indefinido?Si desbloqueas un mutex ya desbloqueado, ¿el comportamiento está indefinido?

El propósito de la pregunta está relacionado con el siguiente código, donde no sé si sería mejor desbloquear los mutexes dentro del bloque if o solo fuera del bloque if.

// This chunk of code makes dual locking semi-autonomous. 
    int c_lckd = 0, q_lckd = 0; 
    if (pthread_mutex_trylock(&crunch_mutex) == 0) c_lckd = 1; 
    if (pthread_mutex_trylock(&queue_mutex) == 0) q_lckd = 1; 
    if (q_lckd && !c_lckd) { QUEUE_UNLOCK; q_lckd = 0; } 
    else if (c_lckd && !q_lckd) { CRUNCH_UNLOCK; c_lckd = 0; } 

    if (c_lckd && q_lckd) { 
     printf("cr = %d, max = %d, cnt = %d\n", 
     crunching, max_crunching, queue_count(conn_queue)); 
     if (crunching < max_crunching && queue_count(conn_queue)) { 
     pthread_t tid = 
      pthread_create(
      &tid, 
      NULL, 
      crunch_conn, 
      (void *)queue_dequeue(conn_queue) 
     ); 
     crunching++; 
     } 
     CRUNCH_UNLOCK QUEUE_UNLOCK 
    } 

Gracias, Chenz

Respuesta

17

Para pthreads resultará en un comportamiento indefinido. Desde la página del manual de pthread_mutex_unlock:

Calling pthread_mutex_unlock() con un mutex que el subproceso de llamada no se sostiene dará como resultado un comportamiento indefinido.

Otros mutex tendrán su propio comportamiento. Como otros han declarado, es mejor leer el manual para cualquier mutex que esté usando.

+0

El resultado depende del tipo de mutex según la página de manual cuyo enlace ha proporcionado amablemente. El comportamiento según la página es 'indefinido' o se devuelve un error. –

1

No es necesario hacerlo de esa manera. Pruebe esto:

// This chunk of code makes dual locking semi-autonomous. 
int c_lckd = 0, q_lckd = 0; 
if (pthread_mutex_trylock(&crunch_mutex) == 0) c_lckd = 1; 
if (pthread_mutex_trylock(&queue_mutex) == 0) q_lckd = 1; 

if (c_lckd && q_lckd) { 
    printf("cr = %d, max = %d, cnt = %d\n", 
    crunching, max_crunching, queue_count(conn_queue)); 
    if (crunching < max_crunching && queue_count(conn_queue)) { 
    pthread_t tid = 
     pthread_create(
     &tid, 
     NULL, 
     crunch_conn, 
     (void *)queue_dequeue(conn_queue) 
    ); 
    crunching++; 
    } 

} 

if (q_lckd) { QUEUE_UNLOCK; q_lckd = 0; } 
if (c_lckd) { CRUNCH_UNLOCK; c_lckd = 0; } 

Es un poco más fácil de seguir y no corre el riesgo de intentar desbloquear un mutex desbloqueado.

+0

¡gracias !, eso lo limpia un poco. Aunque todavía estaría interesado si alguien sabe si el patrón de "desbloqueo y desbloqueo" está explícitamente establecido en el estándar POSIX. –

+0

Sí, al igual que edité mi código para hacerlo. ¡Lo siento! –

+0

Incluso si está especificado en POSIX, evitaría hacerlo: mi experiencia me ayuda a mantenerme alejado de las condiciones más extrañas de las esquinas como esta, incluso si está especificado, me quemó demasiadas veces por menos y luego acepté. bibliotecas para estar cómodo probándolo. –

2

En general, para preguntas como esta, la documentación es la mejor fuente de información. Diferentes mutex pueden comportarse de manera diferente, o puede haber opciones en un único mutex que provoque que se comporte de manera diferente (como en el caso de adquirir recursivamente un mutex en un solo hilo).

7

Como observó Glen, obtendrá un comportamiento indefinido si intenta unlock un mutex desbloqueado - no lo intente. La depuración de subprocesos es suficientemente difícil sin invocar también un comportamiento no definido.

más importante, el estilo de codificación es un poco inusual - ya que no va a hacer nada a menos que obtenga los dos cierres, el código en consecuencia:

if (pthread_mutex_trylock(&crunch_mutex) == 0) 
{ 
    if (pthread_mutex_trylock(&queue_mutex) == 0) 
    { 
     printf("cr = %d, max = %d, cnt = %d\n", 
       crunching, max_crunching, queue_count(conn_queue)); 
     if (crunching < max_crunching && queue_count(conn_queue)) 
     { 
      pthread_t tid; 
      int rc = pthread_create(&tid, NULL, 
           crunch_conn, (void *)queue_dequeue(conn_queue)); 
      if (rc != 0) 
      { 
       // Error recovery 
       // Did you need what was returned by queue_dequeue() 
       // to requeue it, perhaps? 
      } 
      else 
      { 
       crunching++; 
       // Do something with tid here? 
      } 
     } 
     QUEUE_UNLOCK; 
    } 
    CRUNCH_UNLOCK; 
} 

Esto evita los 'lo hice' variables ; también es instantáneamente claro que siempre que las macros de desbloqueo hagan lo que se espera (y no haya excepciones parásitas ni ajustes), los bloqueos automáticos bloqueados se desbloquean. También evita desperdiciar energía al bloquear el mutex de la cola cuando el mutex crunch no está disponible, pero eso es un problema menor en comparación con la claridad añadida.

0

Se debe realizar un bloqueo de exclusión mutua en una secuencia solo si el mismo mutex está bloqueado anteriormente en la misma secuencia. Todos los demás casos tienen un comportamiento no definido según la página del manual.

Si el tipo de exclusión mutua es PTHREAD_MUTEX_DEFAULT, tratando de forma recursiva bloquear los resultados de exclusión mutua en un comportamiento indefinido. Intentando desbloquear el mutex si no estaba bloqueado por el hilo de llamada da como resultado comportamiento indefinido. Intentar desbloquear el mutex si no está bloqueado da como resultado un comportamiento indefinido.

0

Pruébalo. Este es el código de trabajo correcto.

// Mutex is not busy 
if(pthread_mutex_trylock(&object->mtx) == 0) { 
    if(pthread_mutex_unlock(&object->mtx)!=0) { 
     perror("ERRN: pthread_mutex_unlock:"); 
    } 
} 
// Mutex is already busy 
else { 
    if(pthread_mutex_unlock(&object->mtx)!=0) { 
     perror("ERRN: pthread_mutex_unlock:"); 
    } 
} 

// En este punto - nos mutex correctamente desbloqueado.

if(pthread_mutex_destroy(&object->mtx) != 0) { 
    perror("ERRN: pthread_mutex_destroy:"); 
} 
Cuestiones relacionadas