web-dev-qa-db-fra.com

Boost Mutex Scoped_lock

Je lisais un tutoriel Boost Mutex sur drdobbs.com et j'ai trouvé ce morceau de code:

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

boost::mutex io_mutex;

void count(int id)
{
  for (int i = 0; i < 10; ++i)
  {
    boost::mutex::scoped_lock
      lock(io_mutex);
    std::cout << id << ": " <<
      i << std::endl;
  }
}

int main(int argc, char* argv[])
{
  boost::thread thrd1(
    boost::bind(&count, 1));
  boost::thread thrd2(
    boost::bind(&count, 2));
  thrd1.join();
  thrd2.join();
  return 0;
}

Maintenant, je comprends que l'intérêt d'un Mutex est d'empêcher deux threads d'accéder à la même ressource en même temps, mais je ne vois pas la corrélation entre io_mutex et std :: cout. Ce code verrouille-t-il simplement tout dans l'étendue jusqu'à ce que l'étendue soit terminée?

17
Joe Wilcoxson

Maintenant, je comprends que l'intérêt d'un Mutex est d'empêcher deux threads d'accéder à la même ressource en même temps, mais je ne vois pas la corrélation entre io_mutex et std :: cout.

std::cout est un objet global, vous pouvez donc voir cela comme une ressource partagée. Si vous y accédez simultanément à partir de plusieurs threads, ces accès doivent être synchronisés d'une manière ou d'une autre, pour éviter les courses de données et les comportements indéfinis.

Il sera peut-être plus facile pour vous de remarquer qu'un accès simultané se produit en considérant que:

std::cout << x

Est en fait équivalent à:

::operator << (std::cout, x)

Ce qui signifie que vous appelez une fonction qui fonctionne sur le std::cout objet, et vous le faites à partir de différents threads en même temps. std::cout doit être protégé d'une manière ou d'une autre. Mais ce n'est pas la seule raison pour laquelle le scoped_lock est là (continuez à lire).

Ce code verrouille-t-il simplement tout dans l'étendue jusqu'à ce que l'étendue soit terminée?

Oui, il verrouille io_mutex jusqu'à ce que l'objet verrou lui-même soit hors de portée (étant un wrapper RAII typique), ce qui se produit à la fin de chaque itération de votre boucle for.

Pourquoi est-il nécessaire? Eh bien, bien qu'en C++ 11 les insertions individuelles dans cout soient garanties pour les threads, des insertions séparées ultérieures peuvent être entrelacées lorsque plusieurs threads produisent quelque chose.

Gardez à l'esprit que chaque insertion via operator << est un appel de fonction distinct, comme si vous faisiez:

std::cout << id;
std::cout << ": ";
std::cout << i;
std::cout << endl;

Le fait que operator << renvoie l'objet stream vous permet de chaîner les appels de fonction ci-dessus dans une seule expression (comme vous l'avez fait dans votre programme), mais le fait que vous ayez plusieurs appels de fonction distincts reste valable.

En regardant maintenant l'extrait ci-dessus, il est plus évident que le but de ce verrou de portée est de s'assurer que chaque message du formulaire:

<id> ": " <index> <endl>

Est imprimé sans que ses parties soient entrelacées avec des parties d'autres messages.

De plus, en C++ 03 (où les insertions dans cout ne sont pas garanties pour les threads), le verrou protégera le cout objet lui-même contre l'accès simultané.

21
Andy Prowl

Un mutex n'a rien à voir avec quoi que ce soit d'autre dans le programme (à l'exception d'une variable conditionnelle), au moins à un niveau supérieur. Un mutex a deux effets: il contrôle le déroulement du programme et empêche plusieurs threads d'exécuter simultanément le même bloc de code. Il assure également la synchronisation de la mémoire. Le problème important ici est que les mutex ne sont pas associés à des ressources et n'empêchent pas deux threads d'accéder à la même ressource en même temps. Un mutex définit une section critique de code, qui ne peut être entrée que par un thread à la fois. Si toute l'utilisation d'une ressource particulière se fait dans des sections critiques contrôlées par le même mutex, alors la ressource est effectivement protégée par le mutex. Mais la relation est établie par le codeur, en s'assurant que toute utilisation a bien lieu dans les sections critiques.

8
James Kanze