Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt 5 : 5.9.1 - Qt Creator : 4.3.1 - Qt Installer : 2.0.3 - JOM : 1.1.2 - Qt Build suite : 1.7.0 - VS Qt 5 : 2.0.0

Singleton en multi-thread avec QThread, thread-safe ?

Bonjour,

J'ai lu beaucoup de chose sur les singletons en multi-thread. Il existe plusieurs solution, notamment celle du Double Ckeck.
Apparemment, cette solution est thread safe sur certains OS, et pas sur d'autres.
La safety du singleton dépend apparemment directement de l'implémentation même des threads, de leur syncrhonisation et de la gestion individuel de leur mémoire vis-à-vis de celle du thread principal.
Par exemple, le double cjeck ne peut être thread safe en JAVA du à sa gestion de la mémoire apparemment, ce n'est qu'un point noir de JAVA parmi tant d'autres, mais là n'est pas la question
Etant donné que QThread est censé être multi plateforme, ce paramètre de gestion des thread del'OS s'éloigne, mais la question réside toujours :
Est-ce que le double check est Thread Safe avec les QThread ?

Y aurait-il une technique plus appropriée ou plus optimale offerte par les QThread ?

Merci de la réponse, et si quelqu'un a un exemple d'implémentation Qt et non simplement C++ standard.

Hervé

Réponses

  • Salut.
    Qu'es ce que tu appel "double check" en quoi un mutex & co ne te permet pas de protéger ta ressource?
  • Ok.
    Par contre je ne voie pas ce que cela protège réellement. Car à côter de cela c'est les accés concurrent sur le singleton qu'il te faudra protéger.
    La manière la plus simple est de creer le singleton avant de lancer tes thread.

    Sinon, pourquoi as tu besoin d'un singleton?
  • December 2010 modifié
    Si si, cette méthode est une méthode connue pour règler le problème de synchro. Par un mutex, il bloque plusieurs thread qui voudraient initialiser le singleton, et le deuxième check est nécessaire car il permet d'éviter, une fois le premier check+Mutex libéré, de sortir car déjà initialisé.
    Le problème c'est qu'apparemment, cette méthode qui fonctionne très bien sur le papier est parfois dépendant de la plateforme et/ou du compilateur.

    J'ai besoin d'un singleton car je souhaite créer une instance unique de gestion d'un périphérique, mais que cet objet de gestion de périphérique soit intégré dans un CustomWidget. Grosso modo, le but est de créer autant de widget graphique de gestion du dit périphérique que l'ont veut, un seul initialisera l'instance unique (non graphique) de gestion du périphérique. Mais le problème reste le même que la gestion d'interraction avec un base de donnée ou autres, le design pattern du singleton est tant décrié, mais finalement, est la meilleure solution pour beaucoup de problème.
    Maintenant, je suis preneur de mieux, mais comme ça, je ne vois pas trop d'autres solutions.

    Reste le problème du singleton saf thread...
  • pourquoi un thread? la gestion d'un périphérique est bloquante?
    Pourquoi ne pas utiliser les signal/slot entre ton thread et les widget?
  • sans vouloir offenser qui que ce soit, la question de départ est de savoir si l'implémentation d'un singleton un peu évolué tel que le "double check+Mutex" peut être considéré thread safe avec les QThread de Qt et ce en multi plateforme.. car sur le papier, ça l'est, mais dans l'implémentation ça dépend de comment ça se passe en dessous.
    Cela ainsi rappelé, j'en profite pour avouer que je ne comprend pas vraiment ceux qui s'obstinent à répondre directement :

    "un singleton ? mais pourquoi ? t'en a vraiment besoin ? on peut toujours faire autrement..."

    et à ne pas concéder que le Singleton fait parti de la vingtaine de Design Pattern et qu'il répond donc à un besoin précis.
    Beaucoup s'obstineront à réaliser par MACRO ou tout autre périphrase, ou "péricode" les mêmes choses qu'un singleton juste pour se rassurer de ne pas utiliser directement un singleton.
    Le problème du thread safe avec les singletons EST un problème récurrent, ce n'est pas pour ça qu'il est insoluble.
    J'ai fait par exemple l'expérience qu'un singleton template utilisé en héritage comme ici : http://forum.games-creators.org/archive/index.php/t-2559.html
    rend le singleton de base totalement non thread safe, peu importe son implémentation, avec Qt/GCC sous windows...
    le simple fait de l'intégrer directement à la classe le rend déjà beaucoup plus sûr.
    C'est subtile et bon à savoir, je n'irai pas regarder plus en dessous la raison, même si la réponse s'y cache, je ne pense pas avoir les compétences pour déceler quelque problème que ce soit dans l'implémentation même de QThread ou du code assembleur que GCC pourrait créer. Mais je pense que d'autres bien plus compétents que moi se sont déjà posé la question et peut-être puis-je espérer qu'un d'eux soit francophone et traîne sur ce merveilleux forum...

    et pour répondre à ta question yan
    La gestion du périphérique en elle même n'est pas bloquante, c'est un périphérique en réseau, c'est déjà ça, mais j'y joins un autre thread de supervision du même périphérique. Cela fait donc forcément 2 threads distincts, gestion/supervision... plus éventuellement le thread principal si distinct du premier, pour les N widgets graphiques connectés par signaux/slots à la SEULE instance pour ce périphérique.
    L'utilisation d'un singleton pour ce genre d'application a l'énorme avantage de rendre tous objets totalement indépendants, sans se trainer de pointeur à l'initialisation, ou encore pire après initialisation d'objet sur lequel il faudrait perpétuellement venir tester la validité avant de faire quoi que ce soit.

    PS : intéressant QExtend, je vais regarder ça de plus près...
  • December 2010 modifié
    Je n'ai pas dit que tu n'avais pas besoin de singleton mais très souvent ce n'est pas une si bonne solution. Il n'est pas appeler anti-pattern pour rien. Par exemple
    Que se passe t'il le jour tu auras plusieurs périphérique ? Tout le code exploitant le singleton est à refaire?
    Je n'ai pas forcement de meilleur solution, mais d'un point vue code et maintenabilité, je préfère avoir un classe qui représente le périphérique qu'un singleton Après cette classe peut très bien utiliser un singleton en interne. Mais le jour où cela évolue, c'est le code interne de cette classe qui est impacté et non tout le code.

    Pour les threads, perso, si je peux éviter ^^. En générale ça complique le code plus qu'autre chose. Comme ce n’est pas bloquant, je n'ai pas l'impression que tu y gagne grand chose. Après c'est un choix comme un autre et je ne connais pas tous ton projet. Mais personnellement je préfère un fonctionnement asynchrone grâce à l'eventloop de Qt au thread.

    Pour l'histoire des signal/slot (qui est peut être une très bonne solution dans ton cas) je te conseil ces articles: http://qt-labs.developpez.com/#thread
    Je pense que tu peux faire un QObject représentant ton périphérique et rendre son fonctionnement indépendant à l'utilisation de thread ou non grâce au signal/slot.


    Pour répondre à ta question, en cherchant un peu j'ai trouvé cela http://www.bombaydigital.com/arenared/2005/10/25/1 et surtout http://come-david.developpez.com/tutoriels/dps/?page=Singleton
    Intéressant comme problème possible ^^ mais ça me semble très spécifique à la création d'un singleton
  • December 2010 modifié
    Petite question toute conne, mais je me rappelle très bien avoir utilisé un mécanisme sous Visual Studio et compilo Windows SDK qui s'appelle la CRITICAL SECTION (section critique) qui un assure qu'un morceau de code choisi par le développeur à l'aide de 2 MACROS d'entrée et sortie de section que ce bloc de code ne pourra être interrompu dans son exécution par un autre thread, et sera donc exécuté d'un bloc.
    N'est-il pas possible d'avoir de section critique avec GCC ? Qt ?
    Car apparemment, le seule problème avec le double check, c'est de garantir l'ordre entre l'affection du pointeur d'instance unique et la véritable création de l'instance sur laquelle il pointe.
    Selon moi, ce système de section critique résoudrait le problème car il assurerait, en y mettant ET l'affectation ET la création valide de l'instance, qu'à la fin du bloc, le pointeur pointe sur une instance valide, car selon le fonctionnement de la section critique, les 2 opération étant dans le même bloque et exécutées, peu importe l'ordre, mais exécutées sans avoir été interrompue par un autre thread.

    ou alors, faire un double check avec double MUTEX ? chiche? :o)
  • section critique sous widows == mutex chez les autre ^^
    le QMutextLocker + QMutex est la même chose que ta section critique et tes deux macro. L'avantage du QMutexLocker c'est qu'il certifie la libération du mutex lors de sa destruction.
    http://qt.developpez.com/faq/?page=Thread#manipulation-mutex

    Qt propose des opérateurs atomique sur les poiner. Je ne sais si ça peux aider. Au cas ou :
    http://doc.qt.nokia.com/4.7/qatomicpointer.html
  • Et si on remplace tout simplement ça :
    T* T::instance()
    {
    if (smInstance == NULL)
    {
    VMutexLocker lock(&smMutex);

    if (smInstance == NULL) // double-check
    smInstance = new T();
    }

    return smInstance;
    }
    Par ça :
    T* T::instance()
    {
    if (smInstance == NULL)
    {
    VMutexLocker lock(&smMutex);

    if (smInstance == NULL) // double-check
    {
    T* tmp= new T(); // affectation de tmp et allocation de l'instance, peu importe l'ordre
    smInstance = tmp; // ici peu importe l'ordre, l'affectation de smInstance se fera forcément après l'allocation de l'instance faite à la ligne précédente , non ?
    }

    }

    return smInstance;
    }
    Je ne te demande pas une réponse d'expert là dessus yan, ;), puisqu'apparemment tu ne connaissait déjà pas le double check, mais sur le papier, ça résoud pas la problème d'ordre ça ? ça implique, en tous cas pour moi, que l'affectation de tmp et l'allocation de l'instance sont réalisées avant l'affectation de smInstance, ce qui résoud le problème non ?
  • December 2010 modifié
    rv2931 said:
    Je ne te demande pas une réponse d'expert là dessus yan, ;), puisqu'apparemment tu ne connaissait déjà pas le double check
    Suivant les gens ça peut vouloir dire différente chose.
    rv2931 said:
    mais sur le papier, ça résoud pas la problème d'ordre ça ? ça implique, en tous cas pour moi, que l'affectation de tmp et l'allocation de l'instance sont réalisées avant l'affectation de smInstance, ce qui résoud le problème non ?
    Je pense que rien n'empêche au compilateur de virer le tmp vue que pour lui ca peux sembler inutile. C'est une question C++ et non Qt.
  • yan said:
    Je pense que rien n'empêche au compilateur de virer le tmp vue que pour lui ca peut sembler inutile.
    Oui, c'est vrai
    yan said:
    C'est une question C++ et non Qt.
    Oui, c'est vrai :)

    c'est même plus vraiment du C++ mais du ressort du compilateur C++, c'est à dire GCC et son éventuelle optimisation de code... :/
    Là où on en reviendrait à Qt, c'est si on considère que les options par défaut de GCC utilisées par Qt font parti de Qt.

    C'est vrai que l'on sort assez vite des bornes de ce forum concernant ce sujet.
    Je vais étudier la solution du double check+double Mutex(pseudo section critique), ça me paraît être la solution la plus propre et universelle.

    Enfin tout de même, j'ai souvenir qu'une section critique n'est pas tout à fait équivalente à un simple mutex. Un mutex protège de l'exécution d'autre code qui sont eux même protégés par ce même mutex. Dans le cas d'une section critique, c'est tout autre code du processus, donc autres threads, qui est "bloqué" jusqu'à la fin de la section, comme si l'exécution de toute instruction d'un programme était soumise à la libération d'un MUTEX global de section critique, c'est d'ailleurs pour ça qu'il ne faut réellement pas en abuser et minimiser le nombre d'instructions protégées par section critique.
    Les deux font l'affaire dans notre cas, mais bon, c'est pas tout à fait la même chose si je me souviens bien
Connectez-vous ou Inscrivez-vous pour répondre.