2009-11-22 4 views
17

Wenn Sie einen bereits entsperrten Mutex entsperren, ist das Verhalten unsicher, sicher oder undefiniert?Wenn Sie einen bereits entriegelten Mutex entsperren, ist das Verhalten undefiniert?

Der Zweck der Frage bezieht sich auf den folgenden Code, wobei ich nicht weiß, ob es besser wäre, die Mutexe innerhalb des If-Blocks oder direkt außerhalb des If-Blocks zu entsperren.

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

Danke, Chenz

Antwort

17

Bei Pthreads führt dies zu undefiniertem Verhalten. Aus der Manpage für pthread_mutex_unlock:

Aufruf pthread_mutex_unlock() mit einem Mutex, dass der aufrufende Thread in undefiniertem Verhalten nicht führt hält.

Andere Mutexe haben ihr eigenes Behaviour. Wie andere bereits gesagt haben, lesen Sie am besten das Handbuch für den von Ihnen verwendeten Mutex.

+0

Das Ergebnis hängt vom Typ des Mutex ab, wie auf der man-Seite, deren Link du freundlicherweise zur Verfügung gestellt hast. Das Verhalten gemäß der Seite ist entweder 'nicht definiert' oder es wird ein Fehler zurückgegeben. –

1

Sie brauchen nicht, es so zu tun. Versuchen Sie folgendes:

// 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 ist ein wenig leichter zu verfolgen und nicht Gefahr läuft, versucht, ein nicht gesperrtes Mutex zu entsperren.

+0

danke !, das reinigt es ein wenig. Obwohl ich noch interessiert wäre, wenn jemand weiß, ob das "Entsperren und entsperrt" -Muster explizit im POSIX-Standard angegeben ist. –

+0

Ja, genau wie ich meinen Code bearbeitet habe. Es tut uns leid! –

+0

Auch wenn es in POSIX angegeben ist, würde ich es vermeiden - meine Erfahrung führt mich dazu, mich von den seltsamen Eckenbedingungen wie diesem zu distanzieren - selbst wenn es spezifiziert ist, ich wurde zu oft von weniger gebrannt - dann konform Bibliotheken, um es bequem zu versuchen. –

2

Im Allgemeinen ist die Dokumentation für solche Fragen die beste Informationsquelle. Verschiedene Mutexe verhalten sich möglicherweise anders, oder es gibt Optionen für einen einzelnen Mutex, durch die er sich anders verhält (z. B. beim rekursiven Erfassen eines Mutex für einen einzelnen Thread).

7

Wie Glen bemerkte, erhalten Sie undefiniertes Verhalten, wenn Sie versuchen, unlock einen entsperrten Mutex - versuchen Sie es nicht. Das Debuggen von Threads ist hart genug, ohne auch ein undefiniertes Verhalten aufzurufen.

Noch wichtiger ist, die Codierung Stil ein wenig ungewöhnlich ist - da man nichts tun werden, es sei denn, Sie beide Schlösser erhalten, Code entsprechend:

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

Dies vermeidet die ‚habe ich es tun‘ Variablen ; Es ist auch sofort klar, dass solange die Entsperr-Makros das tun, was erwartet wird (und es keine streunenden Ausnahmen oder Setjmps gibt), dass die Mutexe, die gesperrt sind, entsperrt sind. Es vermeidet auch Energieverschwendung beim Sperren des Queue-Mutex, wenn der Crunch-Mutex nicht verfügbar ist - aber das ist ein kleines Problem im Vergleich zu der zusätzlichen Klarheit.

0

Ein Mutex-Entsperren sollte nur dann in einem Thread erfolgen, wenn derselbe Mutex früher im selben Thread gesperrt ist. Alle anderen Fälle sind nicht definiert wie in der Manpage.

Wenn der Mutex Typ PTHREAD_MUTEX_DEFAULT ist, versucht rekursiv die Mutex Ergebnisse in undefinierten Verhalten sperren. Wenn versucht wird, den Mutex freizugeben, wenn er nicht durch den aufrufenden Thread gesperrt wurde, wird undefiniertes Verhalten zurückgegeben. Der Versuch, den Mutex zu entsperren, wenn er nicht gesperrt ist, führt zu undefiniertem Verhalten.

0

Versuchen Sie es. Das ist Code, der richtig arbeitet.

// 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:"); 
    } 
} 

// In diesem Punkt - wir richtig entriegelt Mutex.

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