web-dev-qa-db-fra.com

Essayez catch avec des verrous en C++

En Java:

Lock lock = new ReentrantLock();
try{
  lock.lock();
  someFunctionLikelyToCauseAnException();
}
catch(e){...}
finally {
  lock.unlock();
}

Ma question est avec cet exemple ci-dessus, nous savons que le verrou sera toujours déverrouillé parce que finalement toujours fonctionne, mais quelle est la garantie avec C++?

mutex m;
m.lock();
someFunctionLikelyToCauseAnException();
/// ????

Comment cela fonctionnera-t-il et pourquoi?

37

Pour cela, nous utilisons le style RAII construct std::lock_guard . Quand vous utilisez

std::mutex m;
{ // start of some scope
    std::lock_guard lg(m);
    // stuff
} // end of scope

lg veillera à ce que m soit déverrouillé quel que soit le chemin laissé à la portée, car elle est détruite à la sortie de la portée et le destructeur std::lock_guard appellera unlock

Même si une exception est levée, la pile sera déroulée ( stack removal ) et ce processus détruit lg qui appellera à son tour unlock, garantissant que le verrou est libéré.

61
NathanOliver

quelle est la garantie avec C++?

La garantie pertinente en C++ fonctionne un peu différemment par rapport à celle que vous mentionnez en Java. Au lieu d'un bloc finally, il repose sur la destruction des variables automatiques qui se produit à la sortie de l'étendue, à mesure que le cadre de la pile se défait. Ce pile déroulant se produit quelle que soit la manière dont la portée a été quittée, que ce soit gracefully ou en raison d’une exception.

L’approche privilégiée pour le scénario concernant de tels locks consiste à utiliser RAII , telle que mise en œuvre par exemple par std::lock_guard . Il contient un objet mutex transmis à son constructeur - à l'intérieur duquel il appelle la méthode lock() de mutex, après quoi le thread possède le mutex - et lors du stack () à la sortie de la portée, son destructeur est appelé - dans auquel il appelle la méthode unlock() de la mutex, le relâchant ainsi.

Le code ressemblera à ceci:

std::mutex m;
{
    std::lock_guard lock(m);
    // Everything here is mutex-protected.
}
// Here you are guaranteed the std::mutex is released.
29

Si une exception est levée lors de l'exécution d'un morceau de code protégé par une section critique, c'est-à-dire des codes entre "lock ()" et "unlock ()", cela signifie que l'objet associé sur lequel le morceau de code est utilisé n'est plus dans un état valide. Ceci, peut ou peut ne pas être annulé par le retrait automatique de la pile déclenchée par l’exception, car un effet secondaire peut avoir eu lieu avant que l’exception ne soit levée (un message a été envoyé via socket, une machine commencé, par exemple). À ce stade, le problème majeur n’est pas de savoir si le mutex sera publié (la seule garantie d’utiliser lock_guard à la place). Dans certains cas, il est possible que le mutex toujours verrouillé soit le comportement souhaité et puisse être réinitialisé explicitement une fois que l'appelant a nettoyé le bazar.

Mon argument est le suivant: il ne s'agit pas d'un problème de langue. Aucune fonctionnalité de langue ne peut garantir une gestion correcte des erreurs. Ne prenez pas lock_guard et RAII comme une solution miracle. 

0
John Z. Li