IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo
Sommaire > Le contenu de Qt4 > QtCore > Thread
        Pourquoi ne faut-il pas faire de traitement IHM dans un thread ?
        comment est définie l'appartenance aux threads des objets Qt ?
        comment utiliser les threads avec Qt ?
        comment fonctionne QThread ?
        Comment se passe un connect entre thread ?
        Comment manipuler un mutex ?
        Comment faire une pause dans une QThread ?



Pourquoi ne faut-il pas faire de traitement IHM dans un thread ?
auteur : Yan Verdavaine
La grande majorité des API graphiques des divers OS ne sont absolument pas thread-safe. Le traitement IHM dans différents threads génère des accès concurrents et donc des crashs de l'application.
C'est pour cela que Qt oblige les traitements IHM dans le thread principal (celui exécutant le main() du programme). Attention, cette vérification n'est effectuée uniquement par des assert() en compilation debug.
Si vous devez manipuler l'affichage par un thread, utilisez le système de connexion signal/slot


comment est définie l'appartenance aux threads des objets Qt ?
auteur : Yan Verdavaine
C'est très simple : un objet Qt appartient au thread qui l'a crée. Cela implique tout de même une règle importante : un slot connecté de manière indirecte sera exécuté par l'eventloop du thread auquel l'objet appartient.

Warning : Il est possible de transférer l'appartenance d'un objet entre threads grâce à la méthode moveToThread. Mais ceci est à utiliser avec beaucoup de précaution.


comment utiliser les threads avec Qt ?
auteur : Yan Verdavaine
Qt fournit plusieurs possibilités pour manipuler des threads :
* QtConcurrent : un ensemble d'algorithmes simplifiant énormément l'utilisation des threads. Il partage un pool de threads qui s'adaptera à la machine d'exécution.
* QThread : c'est un classe qui interface un thread. Elle va créer, stopper, faire exécuter sa méthode run() et autres opération sur une thread.
* QThreadPool : pour optimiser la création de threads, il est possible de manipuler un pool de thread avec QThreadPool . Ce pool de threads exécutera des classes héritant de QRunnable et réimplementant la méthode run().


comment fonctionne QThread ?
auteur : Yan Verdavaine
QThread est une interface simple permettant la création et manipulation d'un thread. Elle ne peut être instanciée car la méthode run() est virtuelle pure. Il faut donc créer une classe héritant de QThread et qui réimplémente la méthode run(). Voici les principales fonctionnalités à connaître :

  • run() : méthode exécutée par le thread.
  • exec() : fonction bloquante qui va exécuter une event loop dans le thread. Cette fonction ne peut être appelée que par le run().
  • quit() : slot qui stoppe l'eventloop du thread.
  • start() : slot qui lance un thread qui exécute la méthode run().Cette fonction peut prendre en paramètre la priorité donnée au thread.
  • setPriority() : modifie la priorité d'exécution du thread
  • wait() : fonction bloquante qui attend la fin de l'exécution. Il est possible de spécifier un timeout().
Cette classe émet le signal started() lorsqu'un thread est lancé et finished() lorsque le thread est terminé.

Il existe deux façons de l'utiliser :
  • Sans event loop : seuls des objets n'héritant pas de QObject peuvent être utilisés. C'est à vous de gérer la condition de sortie de la fonction run(). Un signal étant thread-safe, il n'y a aucun problème d'en utiliser dans ce cas.
  • Avec event loop : si le thread doit utiliser des objets héritant de QObject, l'event loop doit être exécutée. Pour cela, il suffit d'utiliser la fonction exec() dans la méthode run().
Avant d'utiliser une QThread, voici quelques règles à bien comprendre :
  • QThread n'est pas un thread et n'appartient pas au thread. Ses slots ne s'exécutent pas dans le thread qu'elle interface.
  • Seule la fonction run() devrait être implémentée. Cette fonction run() est similaire au main() du programme principal. Son exécution correspond au moment où le thread existe réellement.
  • Les QObjects appartiennent au thread qui les créé. Il faut donc créer/supprimer les QObjects utilisés par le thread dans la méthode run().
  • Un signal étant thread-safe, le thread peut utiliser les signaux de la QThread implémentée.
Remarque : Qthread fournit aussi un slot nommé terminate() qui termine brutalement le thread. Cette fonction est à éviter le plus possible.


Comment se passe un connect entre thread ?
auteur : Yan Verdavaine
Par défaut, la connexion entre thread est asynchrone car le slot sera exécuté dans la thread qui possède l'objet receveur. Pour cette raison, les paramètres du signal doivent être copiables. Ce qui implique quelques règles simples :

  • Ne jamais utiliser un pointeur ou une "référence non const" dans les signatures des signaux/slots. Rien ne permet de certifier que la mémoire sera encore valide lors de l'exécution du slot
  • Si il y a une référence const, l'objet sera copié
  • Il est préférable d'utiliser des classes Qt car elles implémentent une optimisation de la copies (cf COW)
Il est possible d'utiliser ses propres classes. Pour cela il faut :

  • Que cette classe ait un constructeur, un constructeur de copie et un destructeur public
  • Il faut l'enregistrer dans les meta type par la méthode qRegisterMetaType
Exemple d'utilisation de qRegisterMetaType
class A
{
public :
    A() {};
    A( const A&a) {...}
    ~A() {...}
 
    ...
}
 
void desConnect()
{
    qRegisterMetaType <A>("A");
   //connection entre deux objets de deux threads
    connect( unObjet, SIGNAL( unSignal(const A &), unObjetDansUneAutreThread, SLOT(monSlot(const A &)));
    connect( unObjet, SIGNAL( unSignal2(A ), unObjetDansUneAutreThread, SLOT(monSlot2(A )));
}
Contrairement aux slots, les signaux sont thread safe et peuvent donc être appelé par n'importe quel thread.


Comment manipuler un mutex ?
auteur : Yan Verdavaine
Pour protéger des données partagées entre threads, Qt fournit la classe QMutex. Cette classe fourni une base :

  • lock : bloque le mutex
  • tryLock : essaie de bloquer le mutex. Possibilité de mettre un timeout
  • unlock : libère le mutex
Afin de simplifier sa manipulation, Qt fournit la classe QMutexLocker basée sur le pattern RAII qui permet de manipuler correctement le mutex et éviter certains problèmes (un thread qui essaye de bloquer deux fois un mutex, un mutex non débloqué suite à une exception,...). QMutexLocker va bloquer le mutex lors de sa création et le libérer lors de sa destruction. Il permet aussi de libérer (unlock()) et de rebloquer (relock()) le mutex.
QMutexLocker vs à la main
QMutex lock;
QMutex lock2;
 
void f()
{
     //locker bloque le mutex lock
     QMutexLocker locker(lock);
     // on bloque le mutex lock2
     lock2.lock();
    
    //fonction qui génère un exeption
     uneFonction();
 
     // fonction non appelée suite à l'exception
     lock2.unlock();
 
    // locker est détruit et va libérer lock
    // malheureusement lock2 est toujours bloqué
}
Lorsqu'une ressource est partagée entre plusieurs threads, ces threads ont le droit d'accéder parallèlement à la ressource uniquement si ils ne font que des accès en lecture. Ainsi, pour optimiser les accès, Qt fournit QReadWriteLock qui est un autre mutex beaucoup plus adapté. Contrairement à QMutex, QReadWriteLock va différencier les 'lock()' en lecture et écriture :

Mutex bloqué en lecture :
  • si un thread essaye de bloquer le mutex en lecture : aucune attente, le thread peut accéder à la ressource
  • si un thread essaye de bloquer le mutex en écriture : le thread attend la libération du mutex
Mutex bloqué en écriture :
  • dans les deux cas le thread attend la libération du mutex
Comme QMutex, cette classe fournit une base :

  • lockForRead() : bloque le mutex en lecture
  • tryLockForRead() : essaie de bloquer le mutex en lecture. Possibilité de mettre un timeout()
  • lockForWrite() : bloque le mutex en écriture
  • tryLockForWrite() : essaie de bloquer le mutex en écriture. Possibilité de mettre un timeout()
  • unlock() : libère le mutex
De la même manière que QMutexLocker, Qt fournie deux classes qui simplifient la manipulation de ce mutex

  • QReadLoker : manipulation du mutex en lecture
  • QWriteLoker : manipulation du mutex en écriture

Comment faire une pause dans une QThread ?
auteur : Yan Verdavaine
Pour diverse raison il peut être intéressant de mettre en pause l'exécution d'un thread sur une durée déterminé. Pour cela la classe QThread fournie plusieurs méthodes static :

  • sleep : pause la thread pendant n secondes
  • msleep : pause la thread pendant n millisecondes
  • usleep : pause la thread pendant n micro secondes


Consultez les autres F.A.Q's


Valid XHTML 1.1!Valid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.