Est-il judicieux de remplacer boost :: thread et boost :: mutex par des équivalents C ++ 11?

153

Motivation: la raison pour laquelle je considère que c'est que mon chef de projet de génie pense que le boost est une autre dépendance et que c'est horrible parce que "vous en dépendez" (j'ai essayé d'expliquer la qualité du boost, puis j'ai abandonné après un certain temps :( Une raison plus petite pour laquelle j'aimerais le faire est que j'aimerais apprendre les fonctionnalités de C ++ 11, car les gens commenceront à écrire du code. Donc:

  1. Existe-t-il un mappage 1: 1 entre #include<thread> #include<mutex>et les équivalents boost?
  2. Considérez-vous une bonne idée de remplacer les éléments boost par des éléments c ++ 11
    . Mon utilisation est primitive, mais y a-t-il des exemples où std n'offre pas ce que fait le boost? Ou (blasphème) vice versa?

PS J'utilise GCC donc les en-têtes sont là.

NoSenseEtAl
la source
46
Les directives de codage de Google de l'OMI sont stupides à bien des égards ... Par exemple. ils n'autorisent pas l'auto à partir de C ++ 11 ... :)
NoSenseEtAl
5
Directives de citation: [auto] entrave la lisibilité [car il supprime] la redondance vérifiée (comme les noms de type) qui peuvent être utiles aux lecteurs.
Andrew Tomazos
31
for (auto it = v.begin () ... :)
NoSenseEtAl
15
@ AndrewTomazos-Fathomling: Vraiment? Personnellement, je ne pense pas m'être jamais soucié du type réel de l'itérateur (enfin peut-être quelques fois), seulement des opérations prises en charge ... Je dirais que la redondance syntaxique est rarement une bonne idée (DRY).
Grizzly
16
btw google a modifié ses directives stupides alors maintenant enfin ils autorisent l'auto
NoSenseEtAl

Réponses:

192

Il existe plusieurs différences entre Boost.Thread et la bibliothèque de threads standard C ++ 11:

  • Boost prend en charge l'annulation de thread, les threads C ++ 11 ne le font pas
  • C ++ 11 prend en charge std::async, mais pas Boost
  • Boost a un boost::shared_mutexpour le verrouillage de plusieurs lecteurs / écriture unique. L'analogue std::shared_timed_mutexn'est disponible que depuis C ++ 14 ( N3891 ), alors qu'il std::shared_mutexn'est disponible que depuis C ++ 17 ( N4508 ).
  • Les délais d'expiration de C ++ 11 sont différents des délais d'expiration de Boost (bien que cela devrait bientôt changer maintenant que Boost.Chrono a été accepté).
  • Certains des noms sont différents (par exemple boost::unique_futurevs std::future)
  • La sémantique de passage d'arguments std::threadest différente de celle utilisée par boost::thread--- Boost boost::bind, qui nécessite des arguments copiables. std::threadautorise les types de déplacement uniquement tels que std::unique_ptrd'être passés comme arguments. En raison de l'utilisation de boost::bind, la sémantique des espaces réservés tels que _1dans les expressions de liaison imbriquées peut également être différente.
  • Si vous n'appelez pas explicitement join()ou detach()alors le boost::threaddestructeur et l'opérateur d'affectation appelleront detach()l'objet thread en cours de destruction / assignation. Avec un std::threadobjet C ++ 11 , cela entraînera un appel à std::terminate()et abandonnera l'application.

Pour clarifier le point sur les paramètres de déplacement uniquement, ce qui suit est C ++ 11 valide et transfère la propriété du intdu temporaire std::unique_ptrau paramètre du f1moment où le nouveau thread est démarré. Cependant, si vous utilisez, boost::threadcela ne fonctionnera pas, car il utilise en boost::bindinterne et std::unique_ptrne peut pas être copié. Il y a aussi un bogue dans la bibliothèque de threads C ++ 11 fournie avec GCC qui empêche cela de fonctionner, comme il l'utilise également std::binddans l'implémentation.

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

Si vous utilisez Boost, vous pouvez probablement passer aux threads C ++ 11 relativement facilement si votre compilateur le prend en charge (par exemple, les versions récentes de GCC sur Linux ont une implémentation presque complète de la bibliothèque de threads C ++ 11 disponible en -std=c++0xmode).

Si votre compilateur ne prend pas en charge les threads C ++ 11, vous pourrez peut-être obtenir une implémentation tierce telle que Just :: Thread , mais il s'agit toujours d'une dépendance.

Anthony Williams
la source
1
Il existe des méthodes de verrouillage / déverrouillage distinctes pour les lecteurs et les écrivains ( lock/ unlockpour les écrivains contre «lock_shared / unlock_shared» pour les lecteurs). Plusieurs lecteurs peuvent appeler lock_shared sans blocage, tant qu'aucun écrivain ne l'utilise.
Dave S
2
Les shared_mutexdocuments se trouvent sur boost.org/doc/libs/1_47_0/doc/html/thread/… . Vous verrouillez le mutex comme partagé ou exclusif, puis utilisez la fonction de déverrouillage correspondante. Vous pouvez également utiliser les types RAII pour ce faire ( shared_lockprend un verrou de lecture partagé lock_guardet unique_lockprend un verrou exclusif). J'ai essayé de clarifier le point sur les types de mouvement uniquement.
Anthony Williams
3
Une autre chose mineure qui m'a fait trébucher: en boost, le destructeur d'un thread en cours le détache ( boost.org/doc/libs/1_47_0/doc/html/thread/… ), tandis qu'en C ++, le destructeur d'un thread en cours appelle se termine () (FDIS 30.3.1.3)
Cubbi
3
Dans C ++ 11, la try_scoped_lockfonctionnalité est couverte par std::unique_lock. Il existe un constructeur qui prend un mutex et std::try_to_lock, et appellera ensuite try_lock()le mutex plutôt que lock(). Voir stdthread.co.uk/doc/headers/mutex/unique_lock/…
Anthony Williams
4
Oui, Boost.Thread est devenu beaucoup plus proche du standard C ++ 11 depuis que j'ai écrit ceci, principalement grâce au travail de Vicente Botet.
Anthony Williams
24

std::threadest largement modélisé d'après boost::thread, avec quelques différences :

  • La sémantique non copiable de boost, mappe à une poignée vers un thread os, est conservée. Mais ce fil est mobile pour permettre le retour du fil des fonctions d'usine et le placement dans des conteneurs.
  • Cette proposition ajoute l'annulation au boost::thread, ce qui est une complication importante. Cette modification a un impact important non seulement sur les threads, mais également sur le reste de la bibliothèque de threads C ++. On pense que ce grand changement est justifiable en raison des avantages.
    • Le destructeur de threads doit maintenant appeler cancel avant de se détacher pour éviter de fuir accidentellement les threads enfants lorsque les threads parents sont annulés.
    • Un membre de détachement explicite est désormais requis pour activer le détachement sans annulation.
  • Les concepts de poignée de thread et d'identité de thread ont été séparés en deux classes (ils sont la même classe dans boost::thread). Cela permet de faciliter la manipulation et le stockage de l'identité des threads.
  • La possibilité de créer un identifiant de thread dont la comparaison est garantie égale à aucun autre thread joignable a été ajoutée ( boost::threadn'a pas cela). C'est pratique pour le code qui veut savoir s'il est exécuté par le même thread qu'un appel précédent (les mutex récursifs sont un exemple concret).
  • Il existe une "porte dérobée" pour obtenir le descripteur de thread natif afin que les clients puissent manipuler les threads en utilisant le système d'exploitation sous-jacent si désiré.

Cela date de 2007, donc certains points ne sont plus valides: boost::threada une native_handlefonction maintenant, et, comme le soulignent les commentateurs, std::threadn'a plus d'annulation.

Je n'ai pas trouvé de différences significatives entre boost::mutexet std::mutex.

Alex B
la source
2
std::threadn'a pas d'annulation; c'est boost::threadça!
Anthony Williams
@Anthony êtes-vous sûr de ne pas vouloir dire interrupt()pour boost :: thread? Il semble également que ce soit une proposition originale, qui a changé depuis 2007.
Alex B
4
Oui, l'annulation dans Boost s'appelle "interruption". Oui, c'est une vieille proposition. Le dernier projet public de la norme C ++ 11 (qui comprend la bibliothèque de threads) est open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Anthony Williams
6

Il y a une raison de ne pas migrer vers std::thread.

Si vous utilisez la liaison statique, std::threaddevient inutilisable en raison de ces bogues / fonctionnalités gcc:

À savoir, si vous appelez std::thread::detachou std::thread::joincela entraînera une exception ou un crash, alors que cela boost::threadfonctionne bien dans ces cas.

ks1322
la source
Je vois qu'un bogue est NON CONFIRMÉ et l'autre est INVALIDE, avec un commentaire disant que le journaliste aurait dû créer un lien libpthread.a. Êtes-vous absolument sûr de ce que vous dites?
einpoklum
1
@einpoklum, vous devriez pouvoir le faire fonctionner en utilisant Wl,--whole-archive -lpthread -Wl,--no-whole-archive, voir cette réponse par exemple stackoverflow.com/a/23504509/72178 . Mais ce n'est pas un moyen très simple d'établir un lien avec libpthread.aet également considéré comme une mauvaise idée.
ks1322
4
Pouvons-nous supposer que ces bogues sont corrigés car nous sommes maintenant en 2016? Les bogues ont été postés en 2012 et à partir de gcc 4.9.2, il prend officiellement en charge C ++ 11, nous ne pouvons donc pas nous plaindre de C ++ 11 avant le support officiel.
Splash
6

Cas d'entreprise

Si vous écrivez un logiciel pour l'entreprise qui doit fonctionner sur une variété modérée à grande de systèmes d'exploitation et, par conséquent, construire avec une variété de compilateurs et de versions de compilateurs (en particulier les versions relativement anciennes) sur ces systèmes d'exploitation, ma suggestion est de rester à l'écart de C ++ 11 au total pour le moment. Cela signifie que vous ne pouvez pas utiliser std::thread, et je recommanderais d'utiliser boost::thread.

Cas de démarrage de base / technique

Si vous écrivez pour un ou deux systèmes d'exploitation, vous savez avec certitude que vous n'aurez jamais besoin de construire qu'avec un compilateur moderne qui prend principalement en charge C ++ 11 (par exemple VS2015, GCC 5.3, Xcode 7), et vous ne l'êtes pas déjà dépend de la bibliothèque de boost, alors std::threadpourrait être une bonne option.

Mon expérience

Personnellement, je préfère les bibliothèques renforcées, très utilisées, hautement compatibles et très cohérentes telles que boost par rapport à une alternative très moderne. Cela est particulièrement vrai pour les sujets de programmation compliqués tels que le threading. De plus, j'ai longtemps connu un grand succès avec boost::thread(et un boost en général) à travers une vaste gamme d'environnements, de compilateurs, de modèles de threading, etc. Quand c'est mon choix, je choisis boost.

Nicholas Smith
la source
1
@UmNyobe Il a raison cependant. De nombreuses implémentations de threads C ++ 11 sont tellement cassées que je suis surpris que les gens envisagent même de l'utiliser.
StaceyGirl
3

Avec Visual Studio 2013, le std::mutexsemble se comporter différemment du boost::mutex, ce qui m'a causé quelques problèmes (voir cette question ).

Robert Hegner
la source
1

En ce qui concerne std :: shared_mutex ajouté dans C ++ 17

Les autres réponses ici donnent un très bon aperçu des différences en général. Cependant, il y a plusieurs problèmes avec std::shared_mutexce boost résout.

  1. Mutices évolutifs. Ceux-ci sont absents de std::thread. Ils permettent à un lecteur d'être mis à niveau vers un écrivain sans permettre à d'autres écrivains d'entrer avant vous . Ceux-ci vous permettent de faire des choses comme pré-traiter un gros calcul (par exemple, réindexer une structure de données) en mode lecture, puis mettre à niveau en écriture pour appliquer la réindexation tout en maintenant le verrou d'écriture pendant une courte période.

  2. Justice. Si vous avez une activité de lecture constante avec a std::shared_mutex, vos rédacteurs seront verrouillés indéfiniment. En effet, si un autre lecteur arrive, il aura toujours la priorité. Avec boost:shared_mutex, tous les threads auront finalement la priorité. (1) Ni les lecteurs ni les écrivains ne seront affamés.

Le tl; dr de ceci est que si vous avez un système à très haut débit sans temps d'arrêt et très controversé, std::shared_mutexne fonctionnera jamais pour vous sans créer manuellement un système de priorité par-dessus. boost::shared_mutexfonctionnera immédiatement, même si vous devrez peut-être le bricoler dans certains cas. Je dirais que std::shared_mutexle comportement de est un bug latent qui attend de se produire dans la plupart des codes qui l'utilisent.

(1) L' algorithme réel qu'il utilise est basé sur le planificateur de threads du système d'exploitation. D'après mon expérience, lorsque les lectures sont saturées, il y a des pauses plus longues (lors de l'obtention d'un verrou d'écriture) sous Windows que sous OSX / Linux.

Robert Fraser
la source
0

J'ai essayé d'utiliser shared_ptr de std au lieu de boost et j'ai en fait trouvé un bogue dans l'implémentation gcc de cette classe. Mon application plantait à cause du destructeur appelé deux fois (cette classe devrait être thread-safe et ne devrait pas générer de tels problèmes). Après être passé à boost :: shared_ptr, tous les problèmes ont disparu. Les implémentations actuelles de C ++ 11 ne sont toujours pas matures.

Boost a également plus de fonctionnalités. Par exemple, l'en-tête dans la version std ne fournit pas de sérialiseur à un flux (c'est-à-dire cout << durée). Boost a de nombreuses bibliothèques qui utilisent ses propres équivalents, etc., mais ne coopèrent pas avec les versions std.

Pour résumer, si vous avez déjà une application écrite à l'aide de boost, il est plus sûr de garder votre code tel quel au lieu de faire des efforts pour passer au standard C ++ 11.

user3323559
la source
4
Le shared_ptrdestructeur n'a pas besoin d'être thread-safe, c'est un comportement indéfini pour qu'un thread accède à un objet pendant qu'un autre thread le détruit. Si vous pensez avoir trouvé un bogue dans shared_ptr de GCC, veuillez le signaler , sinon, selon la balance des probabilités, vous l'utilisez mal.
Jonathan Wakely