1

Bonjour,
J'ai une application écrite en C Win32 API, qui effectue une boucle de traitement assez longue en arrière plan, qui met a jour une barre de progression, et qui éventuellement doit répondre a l'action d'un bouton d'annulation.
Le problème c'est que cela bloque la fenêtre principale et cause des problèmes de rafraichissement graphique.
La seule solution que j'ai trouvé c'est de créer un Thread pour cette boucle, et mettre un WM_TIMER qui regarde de manière périodique si le Thread est terminé pour ensuite redonner la main a certaine fonctions dans la fenêtre.
Est-ce la bonne solution ?
Y a t'il une autre façon de faire qui soit mieux prévue pour ça ?
Je n'ai pas réussi a trouver ce cas la sur Internet pour une solution "officielle", pourtant j'imagine que c'est un cas qui est souvent rencontré.
Merci d'avance pour votre aide.

Voila ma solution pour le moment:
BOOL Thread_Finished; HANDLE hThread; HWND DialogHwnd; void Thread_Start(LPTHREAD_START_ROUTINE function) { EnableWindow(DialogHwnd, FALSE); Thread_Finished = FALSE; hThread = CreateThread(NULL, 0, function, NULL, 0, NULL); } void Thread_End(void) { if (hThread && Thread_Finished) { CloseHandle(hThread); hThread = NULL; EnableWindow(DialogHwnd, TRUE); } } DWORD WINAPI Thread_Read(LPVOID lpParam) { int progress; while (Traitement(&progress)) SendMessage(GetDlgItem(DialogHwnd, IDC_PROGRESSBAR), PBM_SETPOS, progress, 0) Thread_Finished = TRUE; return (0); } BOOL CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: DialogHwnd = hwnd; hThread = NULL; SetTimer(hwnd, 0, 500, (TIMERPROC)NULL); return TRUE; case WM_TIMER: Thread_End(); // Periodically check if a Launched Thread Ended return FALSE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_BTN_READ: Thread_Start(Thread_Read); return TRUE; } return FALSE; } return FALSE; }

2

Pourquoi ne pas conserver ton thread, mais retirer le timer et utiliser à la place SendMessage à la fin de ton traitement pour envoyer un signal qui permettrait au thread de la fenêtre principale de savoir que l'opération est terminée ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

3

C'est ce que j'aurais fait aussi.

Il y a une autre solution qui consiste à utiliser MsgWaitForMultipleObjects(), mais c'est plus compliqué et je ne vois pas d'avantage à l'utiliser.

PS : attention aux variables globales qui sont accédées par plusieurs threads : c'est une cause importante de bugs, à cause des optimisations du compilateur et du fonctionnement des processeurs multicores. Il vaut mieux utiliser les fonctions de communication entre threads, ou si ce n'est pas possible, utiliser des fonctions de synchronisation qui garantissent que tous les accès précédents ont été effectués (memory barriers). Il y a quelques explications ici : (mais oublie la partie sur volatile, c'est une mauvaise idée d'utiliser ce mot-clé avec des threads).
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

Merci pour les infos, je vais regarder ça.
Donc en tout cas, la bonne méthode est bien d'utiliser un thread, pour avoir une partie gestion de la fenêtre et une partie threadé avec gestion du traitement ?

5

Oui smile
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

Question, comment la partie UI donne l’info d’annulation à la boucle de traitement des données ? C’est à elle de checker régulièrement une variable, ou le thread se fait killer ?

7

Tuer le thread est une solution crade, et ça ne garantit pas que les ressources seront libérées correctement.

La variable partagée vérifiée régulièrement est une solution, mais pour les raisons indiquées précédemment, il vaut mieux utiliser un mécanisme de synchronisation explicite, par exemple un événement.

Dans le cas où il y a besoin d'une communication plus évoluée dans le sens thread UI -> thread de calcul, on peut aussi créer une file de messages pour le thread de calcul, qui fonctionne sur le même principe que celle du thread d'UI :
PostThreadMessageA function (winuser.h)docs.microsoft.comPosts a message to the message queue of the specified thread. It returns without waiting for the thread to process the message.

(attention à bien lire la partie "Remarques", il y a une race condition potentielle sur la création de la file)
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

8

Ah ok, merci ! smile

9

Finalement j'ai utilisé SendNotifyMessageA (qui n'est pas bloquant), avec un paramètre WM_USER a la fin de mon thread, et je traite ce message dans ma fenêtre pour réactiver les boutons nécessaires.
C'est vrai que, je me souviens avoir tenter de "Threader" un vieux projet de Raytracer afin de pouvoir calculer plus vite, et en fait j'ai tout casser mon code qui ne fonctionne plus du tout, parceque il a fallu dupliquer des structures de données pour que chaque thread y accède séparément, et ça a pas mal compliquer la chose.
J'imagine que ce n'est pas tellement le fait que ça soit une variable globale qui pose problème, puisque même si je passe l'adresse d'une structure globale en paramètre de la fonction thread, ça ne changera rien, c'est plutôt l'accès concurrentiel aux données qui pose soucis ?
Il faut vraiment que je me penche sur ça pour étudier ce problème.

10

Artemis (./9) :
Finalement j'ai utilisé SendNotifyMessageA (qui n'est pas bloquant), avec un paramètre WM_USER a la fin de mon thread, et je traite ce message dans ma fenêtre pour réactiver les boutons nécessaires.
Si tu veux du non bloquant, le plus simple est PostMessage() (mais SendNotifyMessage() semble faire la même chose si le destinataire du message est dans un autre thread).

Par contre, utilise WM_APP plutôt que WM_USER. Même si la doc n'est pas très claire sur le sujet, il semble que ce soit cette valeur qu'il faille utiliser pour les messages privés dans les applications, et j'ai déjà eu un bug qui était causé par l'utilisation de WM_USER à la place.

Artemis (./9) :
J'imagine que ce n'est pas tellement le fait que ça soit une variable globale qui pose problème, puisque même si je passe l'adresse d'une structure globale en paramètre de la fonction thread, ça ne changera rien, c'est plutôt l'accès concurrentiel aux données qui pose soucis ?
Oui c'est ça : pour faire simple, les accès mémoire ne sont pas toujours faits immédiatement, ni forcément dans l'ordre auquel on s'attend. Donc si tu as besoin que ces effets soient visibles de façon certaine en-dehors de ton thread, il faut des memory barriers.
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

11

Et les MB ne sont pas une garantie parfaite.

C'est lourd, mais si il y a vraiment besoin de synchronisation entre thread et bloquer une structure pendant l'ecriture, les mutex sont vraiment la seule solution:

https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
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.

12

À bas niveau oui, mais en pratique pas besoin de réinventer la roue sur les OS modernes, autant utiliser directement des solutions de plus haut niveau (qui utilisent des mutexes en interne, ou des algos lock-free implémentés soigneusement).
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

13

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.

14

Oui il y a ça, mais aussi plusieurs mécanismes de FIFO (par exemple les pipes) qui sont encore plus simples à utiliser pour implémenter le modèle producteur / consommateur.
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