2017-10-06 8 views
2

J'ai trouvé ce code sur l'échange de pile de revue de code qui implémente un problème producteur-consommateur. Je poste une section de code ici.Deux std :: unique_lock utilisé sur le même mutex provoque un blocage?

Dans le code donné, considérons un scénario quand le producteur produit une valeur en appelant void add(int num), il acquiert verrou sur mutex mu et buffer.size()==size_ ce qui rend le producteur continuer file d'attente en raison de la variable cond conditionnelle.

Au même moment, un changement de contexte a lieu et les appels des consommateurs fonctionnent int remove() à consommer la valeur, il tente d'acquérir le verrou sur mutex mu, mais le verrou a déjà été acquis précédemment par le producteur de sorte qu'il échoue et ne consomme la valeur, provoquant ainsi une impasse.

Où est-ce que je me trompe ici? Parce que le code semble fonctionner correctement lorsque je l'exécute, le débogage ne m'a pas aidé.

Merci

void add(int num) { 
     while (true) { 
      std::unique_lock<std::mutex> locker(mu); 
      cond.wait(locker, [this](){return buffer_.size() < size_;}); 
      buffer_.push_back(num); 
      locker.unlock(); 
      cond.notify_all(); 
      return; 
     } 
    } 
    int remove() { 
     while (true) 
     { 
      std::unique_lock<std::mutex> locker(mu); 
      cond.wait(locker, [this](){return buffer_.size() > 0;}); 
      int back = buffer_.back(); 
      buffer_.pop_back(); 
      locker.unlock(); 
      cond.notify_all(); 
      return back; 
     } 
    } 
+0

wait devrait libérer le verrou jusqu'à ce que la condition soit remplie et le réacquérir avant de quitter attendre –

+1

'while (true) {..; retour;} '... pourquoi utiliser une boucle? – Jarod42

Répondre

3

La réponse de OutOfBound est bonne, mais un peu plus de détails sur exactement ce qui est "atomique" est utile.

L'opération wait sur une variable de condition a une condition préalable et une postcondition que le mutex transmis est verrouillé par l'appelant. L'opération wait déverrouille le mutex en interne et le fait de manière à ne manquer aucune des opérations notify ou notify_all des autres threads résultant du déverrouillage du mutex. A l'intérieur wait le déverrouillage du mutex et l'entrée dans un état en attente de notification sont atomiques l'un par rapport à l'autre. Cela évite les courses de sommeil/réveil.

La forme de section critique conditionnelle teste le prédicat en interne. Cela dépend encore de notifier être fait correctement cependant.

Dans un certain sens, on peut penser à wait comme faisant:

while (!predicate()) { 
    mutex.unlock(); 
    /* sleep for a short time or spin */ 
    mutex.lock(); 
} 

La variable état avec permet d'être notifie efficace la ligne commentée au milieu. Ce qui donne:

while (!predicate()) { 
    atomic { /* This is the key part. */ 
     mutex.unlock(); 
     sleep_until_notified(); 
    } 
    mutex.lock(); 
} 
2

L'idée de std::condition_variable::wait(lock, predicate), est que vous vous attendez que le prédicat soit atteint et que le verrou mutex après. Pour le faire de manière atomique (ce qui est important la plupart du temps), vous devez d'abord verrouiller le mutex, puis l'attendre le relâchera et le verrouillera pour vérifier le prédicat. S'il est satisfait, le mutex reste bloqué et l'exécution continue. Sinon, le mutex sera libéré à nouveau.