Comment faire facilement des std::cout "thread-safe"?
J'ai une application multi-thread, qui utilise fortement std::cout
pour la connexion sans verrouillage. Dans un tel cas, comment puis-je ajouter facilement mécanisme de verrouillage à faire std::cout
thread-safe?
Je ne veux pas de recherche pour chaque occurrence de std::cout
et ajouter une ligne de code de verrouillage. C'est trop fastidieux.
Meilleure pratique?
En fait, c'était un des sujets discutés et donné un exemple dans cette vidéo. L'utilisation de ce qui est à environ 48 minutes en, et la mise en œuvre est un certain temps avant que, comme il est un exemple pour les chaînes avant.
L'article 27.4.1:
Toutefois, la norme dit: "[ Note: les Utilisateurs doivent toujours synchroniser l'utilisation simultanée de ces objets et des ruisseaux par plusieurs threads s'ils veulent éviter entrelacés caractères. — la note de fin ]"
Ce qui revient à ce que j'ai dit: c'est déjà thread-safe, voulez-vous de plus amples sérialiser l'affichage de caractères par chasse?
std::cout
est déjà thread-safe. Voulez-vous dire que vous voulez les vrais morceaux de texte pour être sérialisé par chasse d'eau?L'article 27.4.1:
Concurrent access to a synchronized (27.5.3.4) standard iostream object’s formatted and unformatted in- put (27.7.2.1) and output (27.7.3.1) functions or a standard C stream by multiple threads shall not result in a data race (1.10).
Toutefois, la norme dit: "[ Note: les Utilisateurs doivent toujours synchroniser l'utilisation simultanée de ces objets et des ruisseaux par plusieurs threads s'ils veulent éviter entrelacés caractères. — la note de fin ]"
Ce qui revient à ce que j'ai dit: c'est déjà thread-safe, voulez-vous de plus amples sérialiser l'affichage de caractères par chasse?
OriginalL'auteur xmllmx | 2013-02-05
Vous devez vous connecter pour publier un commentaire.
Je suppose que vous pourriez mettre en place votre propre classe qui encapsule
cout
et associe un mutex. Leoperator <<
de cette nouvelle classe serait de faire trois choses:<<
pour le flux encapsulé et l'argument passéCette classe différente serait de garder la serrure et délégué de l'opérateur
<<
le flux encapsulé. Le destructeur de cette seconde classe finirait par détruire la serrure et la libération du mutex.De sorte que toute sortie vous écrivez comme une seule instruction, c'est à dire qu'une seule séquence de
<<
invocations, sera imprimé automatiquement tant que la totalité de votre sortie passe à travers cet objet avec la même mutex.Appelons les deux classes
synchronized_ostream
etlocked_ostream
. Sisync_cout
est une instance desynchronized_ostream
qui enveloppestd::cout
, puis la séquenceentraînerait les actions suivantes:
synchronized_ostream::operator<<
serait d'acquérir le verrousynchronized_ostream::operator<<
serait délégué à l'impression de "Bonjour", àcout
operator<<(std::ostream&, const char*)
devrait afficher "Bonjour, "synchronized_ostream::operator<<
serait de construire unlocked_ostream
et passer le verrou pour quelocked_ostream::operator<<
serait délégué à l'impression dename
àcout
operator<<(std::ostream&, std::string)
serait d'imprimer le nomcout
se passe pour le point d'exclamation et la fin manipulateurlocked_ostream
temporaire obtient détruits, le verrou est libérécout
, ce qui signifie que vous devez changer toutes les occurrences decout
. Dans ce cas, vous pouvez aussi le remplacer par(cout_lock(), cout)
, oùcout_lock()
est une étendue de verrouillage de type spécifiquement pour la synchronisation de l'accès àcout
. Cela va créer un objet temporaire de séjour jusqu'à ce que la ligne suivante, tandis que le résultat de l'expression(A, B)
estB
en raison de l'opérateur virgule. J'avais prétendre que c'est moins de code, et vous ne pouvez toujours verrouiller le mutex pour plus d'une ligne, si vous le souhaitez.explicitement le maintien d'un
locked_ostream
pour plus d'une seule expression serait possible avec ma configuration, mais votre approche est intéressante. Il ne semble pas se poser pour une macro, mais, ce qui est plutôt de l'onu-C++-ish façon de traiter les problèmes. Sinon, toujours de codage de la serrure devenir fastidieux.Il n'est pas à l'aide d'une macro, il est l'aide de l'opérateur virgule. Peu importe, cela peut être simplifié. Remplacer
cout
avec un objet que les tampons jusqu'à réception d'un'\n'
, et puis verrouille, vide le tampon entier àcout
, et de les déverrouiller tous à la fois.Je le disais macro parce que le même modèle (celui avec la virgule) devrait être répété tous sur la place. Le tampon puis rincer ensemble des lignes de l'approche de sens si seule ligne atomicité est tout ce dont vous avez besoin, mais par exemple, un multi-ligne de trace de la pile entrelacé avec d'autres choses peuvent être source de confusion, ce qui n'est pas toujours la meilleure approche.
OriginalL'auteur MvG
Alors que je ne peux pas être sûr que cela s'applique à chaque compilateur /version std libs
mais dans la base de code que j'utilise std::cout::operator<<() il est déjà thread-safe.
Je suis en supposant que ce que vous êtes vraiment essayer de le faire arrêter std::cout à partir d'un mélange de chaîne lors de la concaténation avec l'opérateur<< plusieurs fois par chaîne, sur plusieurs threads.
La raison chaînes obtenir est brouillé car il y a un "Externe" de la course sur l'opérateur<<
cela peut conduire à des choses comme ce qui se passe.
Si c'est le cas, il est beaucoup plus simple de répondre que de faire votre propre thread-safe de cout ou de la mise en œuvre d'une serrure pour une utilisation avec cout.
Simplement de composer votre chaîne avant de vous passer à cout
Par exemple.
De cette façon, votre piqûres peuvent pas être déformés parce qu'ils sont déjà entièrement formé, plus il est aussi recommandé de bien former vos cordes de toute façon avant de les envoyer.
operator<<(std::ostream&, const std::string&)
atomique. Si il n'y a pas de garanties, la mise en œuvre pourrait encore écrire la chaîne en morceaux dans certaines implémentations, l'entrelacement des données à partir de différents threads.il est garanti pour std::cout (pas de std::ofstream en général).
C'est pire que ça. Je suis en utilisant Xcode (mac OS X) et de voir un caractère par caractère entrelacement, prise de messages et quasiment illisible. Un mutex protégé wrapper pour le cout est probablement la meilleure façon d'aller.
OriginalL'auteur Nickolas George
J'aime vraiment le truc de Nicolás donné dans cette question de la création d'un objet temporaire et de mettre le code de protection sur le destructeur.
Vous pouvez ensuite l'utiliser comme un régulier
std::cout
, à partir de n'importe quel thread:L'objet de recueillir des données de manière régulière
ostringstream
. Dès le coma est atteint, l'objet est détruit et rincer tous les renseignements recueillis.class PrintThreadType {}; PrintThreadType& getPrintThread() {static PrintThreadType a; return a;} PrintThreadType& PrintThread = getPrintThread();
dans un en-tête de contourner l'odr? Probablement ne fonctionne pas.Vous devez être prudent avec cette idée. Les destructeurs en C++ ne devraient pas être autorisés à lancer des exceptions. Par défaut, en C++11 (et, je suppose, au-delà), si une exception se propage d'un destructeur, ce qui fera appel à std::terminate.
OriginalL'auteur Conchylicultor
Depuis
C++20
, vous pouvez utiliserstd::osyncstream
wrapper:http://en.cppreference.com/w/cpp/io/basic_osyncstream
OriginalL'auteur lilezek
Rapide pour le débogage c++11 applications et éviter entrelacés de sortie je viens de d'écrire de petites fonctions comme celles-ci:
- Je utiliser ces types de fonctions pour les sorties et si les valeurs numériques sont nécessaires, je viens de l'utiliser quelque chose comme ceci:
C'est facile et fonctionne très bien pour moi, mais je ne sais pas vraiment si c'est techniquement correcte. Donc, je serais heureux d'entendre vos opinions.
Bien, je n'ai pas lu ceci:
Je suis désolé. Cependant j'espère que cela aide quelqu'un.
Merci beaucoup! J'ai besoin de lire plus à propos des threads.
OriginalL'auteur Germinx
Une solution utilise une ligne de la mémoire tampon pour chaque thread. Vous pouvez obtenir de l'entrelacement des lignes, mais pas entrelacés caractères. Si vous attachez qu'à enfiler-local de stockage, vous pouvez également éviter de verrouiller les problèmes de contention. Alors, quand une ligne est complète (ou encastré, si vous voulez), vous l'écrire sur la sortie standard stdout. Cette dernière opération a bien sûr d'utiliser un verrou. Vous avez des trucs tout cela dans un streambuffer, ce que vous mettez entre les std::cout et c'est original streambuffer.
Le problème ce n'est pas résoudre est des choses comme format de drapeaux (par exemple, hex/dec/oct pour les nombres), ce qui peut parfois s'infiltrer entre les threads, car ils sont attachés à la rivière. C'est rien de mal, en supposant que vous êtes seulement de la journalisation et de ne pas l'utiliser pour les données importantes. Il permet tout simplement pas de format de choses spécialement. Si vous avez besoin d'hex de sortie pour certains numéros, essayez ceci:
Des approches similaires pour les autres formats.
Je ne suis pas sûr de ce que tu veux dire, comme je l'ai dit sur le premier niveau, chaque thread possède sa propre mémoire tampon, donc il n'y a pas de commune du tampon. Sur le deuxième niveau, il y a en effet un tampon commune, mais que le tampon "a utiliser un verrou" comme je l'ai écrit ci-dessus. Les deux est thread-safe pour moi.
Désolé de ne pas la lecture à travers. Mon habitude est de lire les premières phrases et de regarder les exemples de code, et je n'ai pas trouvé tous les verrous. J'ai tendance à traiter le début ou la fin comme un résumé. Et je pense que les réponses fit un rapide coup d'œil est vraiment génial.
OriginalL'auteur Ulrich Eckhardt
Je sais que c'est une vieille question, mais il m'a beaucoup aidé avec mon problème. J'ai créé une classe utilitaire basé sur ce post, les réponses et je tiens à partager mon résultat.
Vu qu'on utilise C++11 ou les dernières versions C++, cette classe fournit print et println fonctions de composer des chaînes avant d'appeler le flux de sortie standard et d'éviter les problèmes de concurrence d'accès. Ce sont variadic fonctions qui utilisent des modèles pour imprimer différents types de données.
Vous pouvez vérifier son utilisation dans un producteur-consommateur problème sur mon github: https://github.com/eloiluiz/threadsBar
Donc, voici mon code:
OriginalL'auteur eloiluiz
Le long de la lignes de la réponse suggérée par Conchylicultor, mais sans hériter de
std::ostringstream
:EDIT: Fixe type de retour de l'opérateur surchargé et a ajouté une surcharge pour
std::endl
.EDIT 1: j'ai étendu ce dans un simple d'en-tête uniquement de la bibliothèque pour la journalisation de débogage des programmes multi-thread.
De sortie:
Fait. J'espère que vous le trouverez utile.
OriginalL'auteur cantordust
En plus de la synchronisation de cette solution fournit des informations sur le fil à partir de laquelle le journal a été écrit.
AVERTISSEMENT: C'est très naïf façon de synchroniser les journaux, mais il peut aussi être applicable pour certains petits cas d'utilisation à des fins de débogage.
Cela peut être utilisé comme cela.
Et il vous donnera la sortie du journal
OriginalL'auteur samvel1024