8
votes

Est-il sûr de mélanger boost :: thread avec C ++ 11 std :: mutex?

Est-il sûr d'utiliser std :: mutex et ses parents dans un programme qui démarre ses threads via boost?

(Utiliser std :: thread n'est pas une option pour moi (je pense), car l'application a besoin de beaucoup d'espace de pile, et sur certaines plates-formes, elle nécessite de remplacer la taille de pile par défaut lors de la création. )


12 commentaires

Oui, c'est le moyen le plus sûr. std :: mutex n'est qu'une couche entre votre programme et les appels d'API du système d'exploitation. Aussi boost :: thread .


Pourquoi ne pas utiliser boost :: mutex si vous êtes inquiet? si vous utilisez les mutex de boost lui-même, vous bénéficiez de toutes ses fonctionnalités que std :: mutex n'a pas. Comme le concept UpgradeLockable


@MrBin Cela ne me semble pas du tout sûr - pas après avoir lu les réponses à cette question connexe .


@Mellester J'essaie de réduire «l'empreinte de la bibliothèque» d'un module principal, afin qu'il puisse être utilisé en dehors de son écosystème actuel avec le moins de bibliothèques non standard possible. Effacer le module de base de boost :: thread serait une énorme victoire à cet égard, notamment car cela éliminerait l'obligation de se lier à une bibliothèque supplémentaire (par opposition à de nombreux autres composants boost qui sont uniquement en-tête).


Il faudrait vraiment s'en tenir à des objets très primitifs pour les serrures. vous ne pouvez pas vraiment être sûr que std :: lock (m1, m2) n'utilise pas quelque chose comme std :: thread :: sleep_until parce que ceux-ci ne sont pas définis dans boos :: thread.


Bonne idée d'essayer de réduire la dépendance à Boost. Mais vous ne devez pas non plus définir la taille de la pile à partir de l'application, il existe probablement une option d'éditeur de liens pour cela pour votre plate-forme spécifique ( exemple , exemple ). Ensuite, vous pouvez passer à std :: thread et ne pas vous soucier de mélanger les bibliothèques.


Êtes-vous sûr que l'application a besoin de beaucoup d'espace de pile? Je ne peux pas voir une application qui remplit 8 Mo d'espace de pile.


@rustyx Dans un monde idéal, nous nous fierions aux paramètres du compilateur ou du système pour contrôler la taille de la pile; mais malheureusement sur l'une de nos plates-formes cibles, la taille de la pile pour les threads supplémentaires ne peut que être contrôlée depuis l'application en cours d'exécution (en vous regardant, OS X).


@fiorentinoing Sur OS X, nous parlons d'une limite de 512 ko par thread, et non de 8 Mo. Et nous avons vu des cas réels où même 4 Mo étaient insuffisants. (Le logiciel a été modifié depuis, et j'espère que nous pouvons le faire maintenant avec 512kiB, mais les tests sont toujours en attente.)


@ChristophLipka Je voudrais essayer valgrind (plugin massif) pour comprendre où la pile est utilisée et changer en tas avec RAII. Avez-vous cela?


@fiorentinoing Je suis un jockey Windows; valgrind est au-delà de mon salaire;) Mais oui, se débarrasser de plus de structures monopolisant la pile est une option que je peux envisager. Mais ce ne sera probablement pas aussi trivial que de simplement l'allouer sur le tas via un simple wrapper RAII, en raison de contraintes de performances, donc j'essaie toujours d'éviter d'ouvrir cette boîte de vers.


Les premiers résultats des tests sont maintenant et ils semblent très prometteurs: dans un cas qui nécessitait auparavant un énorme 2 Mio de pile, nous nous en tirons maintenant avec 16 Kio. Donc, à moins que quelque chose d'inattendu survienne dans d'autres tests, la taille de pile par défaut devrait être suffisante pour tout le monde, même sous Mac OS X.


3 Réponses :


0
votes

l'application a besoin de beaucoup d'espace dans la pile, et sur certaines plates-formes, elle nécessite de remplacer la taille de pile par défaut lors de la création

std :: thread utilise pthread_create pour créer des threads sur les plates-formes prenant en charge POSIX. Une autre option pour vous est de remplacer pthread_create , de définir la taille de la pile et d'appeler le pthread_create d'origine.

Un exemple Linux fonctionnel (je n'ai pas accès à MacOS pour l'essayer):

int {anonymous}::pthread_create_override(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)
main()::<lambda()>

Sorties:

#include <cstdio>
#include <thread>
#include <dlfcn.h>

namespace {

size_t const STACK_SIZE = 8 * 1024 * 1024;

int pthread_create_override(pthread_t* thread, pthread_attr_t const* attr, void*(*start_routine)(void*), void* arg) noexcept {
    std::printf("%s\n", __PRETTY_FUNCTION__);

    pthread_attr_t attr2;
    if(attr)
        attr2 = *attr;
    else
        if(pthread_attr_init(&attr2))
            std::abort();

    size_t stacksize = 0;
    pthread_attr_getstacksize(&attr2, &stacksize);
    if(stacksize < STACK_SIZE) {
        if(pthread_attr_setstacksize(&attr2, STACK_SIZE))
            std::abort();
    }

    static auto const real_pthread_create = reinterpret_cast<decltype(&pthread_create)>(::dlsym(RTLD_NEXT, "pthread_create"));
    int rc = real_pthread_create(thread, &attr2, start_routine, arg);

    if(!attr)
        pthread_attr_destroy(&attr2);

    return rc;
}

} // namespace

extern "C" {

int pthread_create(pthread_t* thread, pthread_attr_t const* attr, void*(*start_routine)(void*), void* arg) {
    return pthread_create_override(thread, attr, start_routine, arg);
}

} // namespace

int main() {
    std::thread t([]() { std::printf("%s\n", __PRETTY_FUNCTION__); });
    t.join();
}

7 commentaires

Les approches ulimit et setrlimit sont connues pour n'affecter que le thread principal sous Mac OS X.


Si std :: thread appelle pthread_create - et encore moins si cet appel est implémenté de telle manière qu'il peut simplement être remplacé en fournissant mon propre code > - est très certainement défini par l'implémentation, et compter sur elle pour rester implémenté de cette manière pose des problèmes.


@ChristophLipka Pouvez-vous vérifier sur votre système Mac OS si le remplacement de pthread_create résout votre problème? Votre bibliothèque standard utilise très probablement le standard pthread_create , car std :: thread a été conçu comme un wrapper Pthread et Mac OS X fournit cette API.


pouvez-vous fournir une source faisant autorité pour votre affirmation selon laquelle std :: thread a été "conçu comme un wrapper Pthread"? Parce que je suis à peu près sûr que le standard n'a pas été spécifiquement conçu comme un wrapper pour une implémentation de threads particulière . (Et non, je ne peux pas vérifier sur "mon système Mac OS", car je n'ai pas moi-même un tel système à ma disposition; encore moins que Mac OS n'est pas notre seule plate-forme cible.)


@ChristophLipka Il est de notoriété publique que le thread C ++ imite l'API Pthread, et cela est évident pour quiconque connaît les deux API.


Si std :: thread imite l'API Pthread, où sont les attributs de thread? Je ne les vois nulle part.


@ChristophLipka pubs.opengroup.org/onlinepubs/9699919799/help/codes. html # TSS : Attribut de taille de pile de threads La fonctionnalité décrite est facultative. La fonctionnalité décrite est également une extension de la norme ISO C.



0
votes

La façon dont vous démarrez les threads ( pthread_create , boost :: thread , std :: thread ) est orthogonale aux primitives de synchronisation que vous utilisez ( std vs boost vs Intel TBB vs libatomic vs etc.).

Sinon, vous ne pourrez pas mélanger et associer des bibliothèques utilisant ces différentes API dans une seule application.

Les primitives de synchronisation de bas niveau telles que les atomes, les mutex et les variables de condition peuvent être placées dans la mémoire partagée et utilisées par différents processus sans créer explicitement de threads supplémentaires. De plus, le thread d'application principal est créé par le noyau du système d'exploitation pour vous sans utiliser aucune des API de l'espace utilisateur.


0 commentaires

1
votes

Oui, vous pouvez utiliser std :: mutex dans les threads créés avec boost :: thread .


0 commentaires