1

yop,

J'ai des petits soucis pour designer un programme multi-thread...

Typiquement, dans mon thread principal, j'ai un objet Thread, qui a une méthode run() (le worker).
Mon objet Thread a aussi une méthode isRunning().

Je suis emmerdé par l'intervalle de temps situé entre l'appel à run() et le moment où Thread répondra Vrai à la question isRunning().
Parceque si j'ai un autre thread qui pose la question dans ce laps de temps, il pensera que Thread est à l'arrêt alors que ce n'est pas vrai.

Comment gérer ce genre de cas ? Une espèce de mutex général pour dire "tout le monde s'arrête, un thread se lance, on attend que ça se stabilise puis on débloque" ? Complètement merdique et overkill.

Alors comment s'y prendre ?

Merci d'avance. smile

2

C'est compliqué.

Quand tu créé un thread, tu n'a aucune garantie qu'il va être créé de suite, il peux se passer un certain temps entre l'appel a la fonction de création, et la création en elle même.

Comment "isRunning" fonctionne?

J'espere que ce n'est pas un variable dans ton objet qui est accédé dans le thread, et aussi en dehors. Pour un boolean c'est peut etre pas super grave, mais si tu ne protege pas l'acces a cette variable (mutex en l'occurence, ou un semaphore suivant le modele de consommation, sachant que tu peux avoir un modele avec plusieurs lecteurs en meme temps, et obliger a avoir qu'un seul writer a la fois qui bloque les lecteurs le temps de l'écriture)

A tu vraiment besoin de lire si le thread est en train de fonctionner?
A vrai dire il n'y a pas de moyen fiable/propre dans les APIs pour savoir si un thread a été créé et tourne, tu peux utilise join, mais ca va bloquer tant que le thread "rejoint" ne quitte pas.

Pourquoi tu ne mettrais pas ton drapeau a "creating" avant de lancer le thread_create, donc tu sais que le thread est pas encore lancé, mais il est pas mort non plus, et le code du thread met a running des qu'il commence?

Ceci dit quel est le besoin de connaitre l'état du thread?
Si vraiment tu as besoin d'attendre que le thread a terminé, Join est fait pour ca.

Sinon si ton code s'y prete pour faire certains calculs en multithread, je te conseille de jeter un oeil a OpenMP, c'est un truc magique qui fait le threading pour toi!
Marche plutot bien pour un raytracer par exemple. Mais ce n'est pas applicable a tout malheureusement.
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

3

C'est marrant, j'ai une impression de déjà-vu grin

Je ne sais pas quel OS et API tu utilises (Qt j'imagine).

Sous Windows, pour gérer ce cas, j'utilise un event. À la création, il est à l'état inactif, et c'est le thread qui le fait passer à l'état actif dès qu'il est prêt. Le programme principal a juste à faire un WaitForSingleEvent pour se synchroniser. C'est généralisable à plusieurs threads avec WaitForMultipleEvents.

Qt a très probablement un truc similaire, et au pire tu peux aussi faire que ton event envoie un signal au programme principal pour dire qu'il est prêt, par exemple.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

4

Merzil pour la réponse !

Déjà tu me rassures, ce n'est pas évident. Deux jours que je me déchirais les neurones en me disant que je ratais un truc, ouf grin

Ensuite, côté API, j'utilise les threads proposés par Qt : https://doc.qt.io/qt-5/qthread.html
Les méthodes à clés sont :
start() : initialise et démarre le worker. C'est sûrement lui qui s'occupe de toute la mouise platform-spécifique.
run() : c'est le worker (méthode virtuelle), tu le redéfinis pour y faire ce que tu veux dans ton thread
isRunning() : la méthode dont je parlais au-dessus.

Mon exemple concret : une recherche en temps réel quand l'utilisateur tape des mots dans une barre de recherche.
Ca a l'ai lambda, mais...

Quand run() se termine parce qu'il a fini la recherche (à la toute fin, c'est à dire que l'OS dégage sa pile avec ses vars locales, il n'a plus qu'à faire le rts final à faire), et que des nouvelles données de recherche sont fournies à l'objet propriétaire du thread, il y a une chance pour que le thread réponde encore Vrai à isRunning, et donc ignore l'appel à start() dû aux données fraichement arrivées.
Donc il y a un risque que le thread s'arrête, pensant avoir fini, alors qu'il reste des données à traiter.

Les workarounds ne manquent pas, à base de timer pour relancer le thread une fois de temps en temps. Mais un artiste se refuse à ça embarrassed

edit -> cross. Je lis, j'analyze, et on en parle. Mais déjà, merci cheeky

5

Donc si je comprends bien, tu veux réutiliser le même thread pour la nouvelle recherche ?

Dans ce cas, pourquoi ne pas le laisser tourner tout le temps, plutôt que de l'arrêter à chaque fois ? C'est plus simple, et le coût est négligeable : si tu codes proprement, ton thread n'utilisera pas de CPU quand il n'a rien à faire.
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

6

Ah tiens, comment tu le mets en pause ton thread ? int i = 0; while (i++ < 100000000); ? grin

Oui, je pensais réutiliser le même thread.
Tu me donnes une bonne idée à creuser : arrêter le thread en cours tout en en lançant un autre.

7

Passage de messagse
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

8

Folco (./6) :
Tu me donnes une bonne idée à creuser : arrêter le thread en cours tout en en lançant un autre.
Vu que la création et la destruction de threads ont un coût (pas très élevé, mais pas nul), je ne vois pas vraiment l'intérêt de faire ça, à part si ton thread est un peu long à la détente. Ça a également l'inconvénient (au moins en théorie) de te retrouver avec plein de threads si les requêtes sont trop rapides.

Folco (./6) :
Ah tiens, comment tu le mets en pause ton thread ?
Facile. Il suffit d'appeler une fonction avec une attente synchronisée, par exemple le changement d'état d'un mutex, ou la lecture du prochain message dans une queue. Tant que ça attend, le thread est en pause (sauf si l'implémentation du threading est vraiment faite avec les pieds, mais c'est pas ton cas).
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

9

Ok merci. Oui, c'est mal vu l'entassement de threads.

Je fais plus simple pour le moment :
    requestInterruption();
    while (isRunning())
        ;
Et je relance derrière. Le thread regarde une fois de temps en temps si on lui a demandé de se couper.

10

En fait le multithread, c'est comme l'objet, tu ajoute pas ca comme ca, il faut que ton projet soit organisé autour, sinon ca peux etre assez compliqué
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

11

Complètement. J'ai plusieurs structures de données à modifier. Mais c'est pour le fun alors ça va.
Et la recherche à la volée, ça roxe sa race, donc ça vaut le coup cheeky

12

^^
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

13

Folco (./9) :
    while (isRunning())
        ;
Vade retro !!!

(Regarde si tu n'as pas une fonction d'attente de fin de thread, ça existe au moins sous Windows. Au pire, rajoute un sleep de 100 ms dans ta boucle ou quelque chose comme ça, histoire de pas bouffer 100% de CPU pour des prunes)
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

14

Ok ça roule, Qt fournit ça ^^
Et sinon oui, je peux me câbler sur le signal de fin de thread. Mais toujours pareil en multi-thread : le temps de faire le cablage, le message ne sera-t-il pas déjà passé ? grin

15

pthread_join()

Si Qt n'a pas acces a cette fonction, leur multithreadiing est fait avec les pieds
avatar
Proud to be CAKE©®™


GCC4TI importe qui a problème en Autriche, pour l'UE plus et une encore de correspours nucléaire, ce n'est pas ytre d'instérier. L'état très même contraire, toujours reconstruire un pouvoir une choyer d'aucrée de compris le plus mite de genre, ce n'est pas moins)
Stalin est l'élection de la langie.

16

https://doc.qt.io/qt-5/qthread.html#wait
En effet, c'est exactement ça qu'il me faut, merci beaucoup hehe

17

Zerosquare (./13) :
Vade retro !!!
Ah oui j'ai eu peur en lisant ça, j'ai cru que personne n'avait relevé pendant quelques posts grin

18

C'est sûr, vous avez complètement raison. Surtout que c'est le thread principal, celui de l'UI. Mais je pense pas que ça aurait bloqué le thread plus d'un millième de seconde, car le worker regarde très souvent si on essaye de l'interrompre.
Mais sur le principe, vous avez complètement raison. smile

19

Même avis que les autres: pour faire ça, je garderais en permanence le worker thread qui serait en attente la plupart du temps, et quand il y a besoin, je lui donnerais à travailler et lui signalerais qu'il a du travail.
Avec Qt, en utilisant une file de tâches accédée sous mutex, et une paire signal/slot "tiens, du travail est devenu disponible". Pour le retour asynchrone de résultat, je dirais une file de résultats (y compris erreurs) accédée sous mutex et une paire signal/slot "tiens, j'ai fini de travailler pour telle requête".
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

20

Yep, je me dirige vers ça en effet, merci bien smile

21

Et pour faire un arrêt propre du worker thread quand l'appli est arrêtée, il y a diverses méthodes, par exemple une paire signal/slot qui définit une variable de sortie dans le worker thread, variable utilisée par les autres endroits du worker thread.
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.