===== Ré-entrance et thread-safety ===== Dans la documentation, les termes //ré-entrance// et //thread-safe// sont employés pour évaluer de quelle manière les classes et les fonctions peuvent être utilisées dans les applications multi-thread. ==== Introduction ==== * Une fonction //thread-safe// peut être appelée simultanément depuis de multiples threads, même si les invocations utilisent des données partagées, car la totalité des références sur les données partagées sont sérialisées. * Une fonction //ré-entrante// peut aussi être appelée simultanément depuis de multiples threads, mais uniquement si chaque invocation utilise ses propres données. Par conséquent, une fonction //thread-safe// est toujours //ré-entrante//, mais une fonction //ré-entrante// n'est pas toujours //thread-safe//. Par extension, une classe est dite //ré-entrante// si ses fonctions membres peuvent être appelées de manière sûre depuis de multiples threads, tant que chaque thread utilise une instance //différente// de la classe. La classe est //thread-safe// si ses fonctions membres peuvent être appelées de manière sûre depuis plusieurs threads, même si la totalité des threads utilisent la //même// instance de la classe. Les classes C++ sont souvent ré-entrantes, simplement parce qu'elles accèdent uniquement à leurs propres données membres. Tout thread peut appeler une fonction membre sur une instance d'une classe ré-entrante, tant qu'aucun autre thread n'appelle une fonction membre de la //même// instance de la classe en même temps. Par exemple, la classe ''Counter'' ci-dessous est ré-entrante : class Counter { public: Counter() { n = 0; } void increment() { ++n; } void decrement() { --n; } int value() const { return n; } private: int n; }; Cette classe n'est pas thread-safe : si plusieurs threads tentent de modifier la donnée membre ''n'', le résultat sera indéfini. Ceci est dû aux opérateurs ''++'' et ''- -'' qui ne sont pas toujours atomiques. En effet, ceux-ci s'incrémentent selon trois instructions/code machine : - Chargement de la valeur de la variable dans un registre. - Incrémentation ou décrémentation de la variable du registre. - Stockage de la valeur du registre dans la mémoire principale. Si un thread A et un thread B chargent simultanément l'ancienne valeur de la variable, incrémentent leur registre, puis la stockent de nouveau, ils finissent par s'écraser les uns les autres. Au final, la variable n'est incrémentée qu'une seule fois ! Clairement, l'accès doit être sérialisé : le thread A doit exécuter les étapes 1, 2 et 3 sans interruption (atomiquement) avant que le thread B ne puisse exécuter les mêmes étapes ; ou vice versa. Une manière simple de créer une classe thread-safe est de protéger tous les accès aux données membres avec un [[QMutex]] : class Counter { public: Counter() { n = 0; } void increment() { QMutexLocker locker(&mutex); ++n; } void decrement() { QMutexLocker locker(&mutex); --n; } int value() const { QMutexLocker locker(&mutex); return n; } private: mutable QMutex mutex; int n; }; La classe [[QMutexLocker]] verrouille automatiquement le mutex dans son constructeur et le déverrouille lorsque le destructeur est appelé, à la fin de la fonction. Le verrouillage du mutex assure que l'accès depuis différents threads sera sérialisé. La donnée membre ''mutex'' est déclarée avec le qualificateur ''mutable'' car nous avons besoin de verrouiller et de déverrouiller le mutex dans ''value()'', qui est une fonction constante (const). Beaucoup de classes de Qt sont //ré-entrantes//, mais elles ne sont pas rendues //thread-safe// car car cette transformation conduirait à s'exposer à des répétitions de verrouillage et de déverrouillage supplémentaires d'un [[QMutex]]. Par exemple, [[QString]] est réentrant mais pas thread-safe. Simultanément depuis plusieurs threads, vous pouvez accéder de manière sûre à des instances //différentes// de [[QString]], mais pas à une //même// instance (excepté si vous protégez l'accès vous-même avec un [[QMutex]]). Quelques classes et fonctions de Qt sont thread-safe. Elles sont principalement des classes apparentées à des threads (par exemple, [[QMutex]]) et à des fonctions fondamentales (par exemple, [[QCoreApplication#postEvent|QCoreApplication::postEvent()]]). **Note :** Les classes de Qt ne sont documentées comme //thread-safe// seulement si elles sont destinées à être utilisées dans de multiples threads. **Note :** La terminologie dans le domaine du multithreading n'est pas entièrement standardisée. POSIX utilise les définitions de ré-entrante et de thread-safety qui sont quelque peu différentes pour son API C. Lors de l'utilisation d'autres bibliothèques de classes C++ orientées objet avec Qt, assurez vous que les définitions soient comprises. ==== Remerciements ==== Merci à pour la traduction et à ainsi qu'à pour leur relecture !