Sat83
Messages postés166Date d'inscriptionmardi 11 novembre 2003StatutMembreDernière intervention13 octobre 2008
-
21 avril 2008 à 18:06
Rematrix
Messages postés115Date d'inscriptionjeudi 17 avril 2008StatutMembreDernière intervention16 juillet 2008
-
23 avril 2008 à 12:09
Bonjour a tous,
J'ai une petite interrogation concernant les threads.
Je souhaite arreter mon application a une heure précise. Mon programme fonctionne parfaitement, et actuellement j'utilise un TTimer avec un Interval de 10 secondes qui regarde que l'heure fixée n'est pas arrivée. Si elle est arrivée, l'application se ferme.
Jusqu'ici tous va bien. Le problème c'est que si mon application plante ou reste bloqué dans un traitement (ca ne devrait pas arrivé, mais on ne sais jamais), elle restera bloqué et elle ne se fermera pas a l'heure (puisque l'application etant planté, elle n'ira pas dans la procédure du Timer).
En gros je voudrais que le thread tourne en boucle en attendant la bonne heure, et que lorsque celle ci arrive, elle ferme l'application ou force la fermeture si l'appli est plantée.
Je me demande donc si je remplace la fonction du Timer par un thread
parallèle, est ce que cela marchera même si le thread principale est
planté? Ou est ce que cette solution ne vous semble pas correcte (je connais pas encore bien les threads et leurs comportements)?
type
TAutoTerminateThread = class(TThread)
private
FMainWndHandle: THandle;
FTerminateTime: TDateTime;
FWaitEvent: Cardinal;
protected
procedure Execute; override;
public
constructor Create(MainWindowHandle: THandle; EndTime: TDateTime);
destructor Destroy; override;
{ Obligé de réintroduire cette méthode car on utilise un TEvent,
ce qui n'est malheureusement pas géré nativement par Delphi }
procedure Terminate; reintroduce;
end;
constructor TAutoTerminateThread.Create(MainWindowHandle: THandle;
EndTime: TDateTime);
begin
inherited Create(False);
FreeOnTerminate := True;
FMainWndHandle := MainWindowHandle;
FTerminateTime := EndTime;
FWaitEvent := CreateEvent(nil, True, False, nil);
if FWaitEvent = 0 then
RaiseLastOSError;
end;
destructor TAutoTerminateThread.Destroy;
begin
CloseHandle(FWaitEvent);
inherited Destroy;
end;
procedure TAutoTerminateThread.Terminate;
begin
SetEvent(FWaitEvent);
inherited Terminate;
end;
procedure TAutoTerminateThread.Execute;
var
WaitTime: Cardinal;
begin
WaitTime := Trunc(FTerminateTime - Now) * 1000;
if WaitForSingleObject(FWaitEvent, WaitTime) = WAIT_TIMEOUT then
begin
{>> On n'a fait pas appel à Terminate et le temps s'est écoulé: on quitte }
{ D'abord, la méthode "douce" (pas SendMessage car si l'autre thread
est bloqué, celui-ci aussi va se bloquer) }
PostMessage(FMainWndHandle, WM_CLOSE, 0, 0);
{ On attend encore un peu pour voir si l'autre thread fait
appel à Terminate ou s'il est vraiment coincé (5 secondes maxi) }
if WaitForSingleObject(FWaitEvent, 5000) = WAIT_TIMEOUT then
begin
{ Coinçage total: suicide obligé ! }
Beep;
TerminateProcess(GetCurrentProcess, 1);
end;
end;
end;
procedure TFrmTest.FormCreate(Sender: TObject);
begin
{>> Le programme va se fermer 5 secondes après son ouverture }
FTermThread := TAutoTerminateThread.Create(Handle, Now + 5.0);
end;
procedure TFrmTest.FormDestroy(Sender: TObject);
begin
{>> On signale au thread de se terminer: la procédure WaitForSingleObject
renvoit immédiatement WAIT_OBJECT_0 et le thread se détruit normelement }
FTermThread.Terminate;
end;
procedure TFrmTest.BtnQuitClick(Sender: TObject);
begin
Close;
end;
procedure TFrmTest.BtnPlanteClick(Sender: TObject);
begin
while True do
Sleep(10);
end;
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 201918 22 avril 2008 à 14:01
Bein, si le panel qui a testé est content, on est contents aussi, nous... ;)
Pour info, il y a une variante à
WaitForSingleObject. C'est l'utilisation de GetExitCodeProcess qui renvoit le code STILL_ACTIVE tant que le processus est actif... Mais bon! Ca n'apporterait rien dans le cas présent.
Sinon, j'ai pensé à une autre stratégie, sans Thread :
-
Lancer un processus-Timer, uniquement chargé d'attendre l'heure de fermeture. Et, ce serait ce processus qui lancerait ton application avec CreateProcess. A l'heure de fermeture, ce processus-parent mettrait fin à son processus-fils (ton appli).
... Voir si ça apporte un avantage. A votre avis?
PS:
@Flo
« Mais TerminateProcess() libère quand même toutes les ressources »
Oui, mais lorsqu'on met fin à un processus le système met aussi fin à ses Threads. Et ces Threads peuvent manipuler des objets partagés qui risquent de ne pas être libérés correctement, eux...
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 22 avril 2008 à 15:40
Je ne vois pas à quoi servirait d'appeler EnumThreadWindows puisque l'envoi d'un message à la fenêtre principale suffit.
@Cari:
Très juste pour le GetExitCodeProcess() mais comme tu le dis, dans tous les cas il faudrait bien attendre une certaine durée avant de tester donc ça revient au même.
Pour TerminateProcess(): lorsqu'un processus est fermé, toute la mémoire utilisateur est libérée (même si les FreeMem, Dispose ou Destroy n'ont pas été appelés) et tous les Handles ouverts sont automatiquement fermés (ceux des fenêtres, des fichiers, des sémaphores, des objets GDI, ...) et la mémoire associée est libérée.
Si tes threads partagent des objets entre eux, ces objets seront détruits comme les autres.
Les seuls soucis qui interviennent sont :
- Aucune procédure de sauvegarde de quelconque donnée n'est appelée.
- Les liaisons réseau sont brutalement coupées (imagine la tête du gars de l'autre côté du fil ^^)
- Si tu as des régions globalement partagées (file mapping par exemple): elles ne sont libérées que lorsque tous les processus qui les utilisent sont fermés. Entre temps, ceux qui restent peuvent lire des données dedans qui risquent d'être corrompes.
Mais bon, quand ton processus est bloqué, je pense qu'il est préférable de le débloquer, coûte que coûte, plutôt que de le laisser consommer ses 100% de ressources CPU et recevoir une facture edf bien gonflée !
Dans ce sens, Sat83 a tout à fait raison de vouloir se prémunir de ce genre de dégâts.
Sinon, comme autre solution: lorsque l'appli est plantée, elle peut demander à l'utilisateur de faire Ctrl+Alt+Suppr, non !? ;-)
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 21 avril 2008 à 18:38
Salut !
Si ton thread principal est planté (genre boucle infinie) ou ne vérifie pas périodiquement l'existence de signaux provenant de l'autre thread, ta solution ne fonctionnera pas.
Cette solution que tu proposes est donc strictement identique à l'autre.
Si ton application est déjà multi-threadée, c'est la solution que je te conseille, sinon tu peux largement garder la version "timer" qui est tout à fait acceptable.
Ce que tu peux faire, par contre, c'est changer la propriété Interval du timer en fonction du temps restant avant la fermeture, pour moins consommer en ressources processeur (même si c'est pas énorme une fois toutes les 10 secondes)
Un truc du genre en gros :
var
TempsRestant: Double;
begin
TempsRestant := DateTimeDeFin - Now;
if TempsRestant <= 0 then
Close
else if TempsRestant <= 60 then
Timer.Interval := 5000
else if TempsRestant <= 120 then
Timer.Interval := 40000
else
Timer.Interval := 120*60;
end;
A+
Flo
Vous n’avez pas trouvé la réponse que vous recherchez ?
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 21 avril 2008 à 18:39
J'avais pas vu un détail: "forcer la fermeture"
Dans ce cas, alors tu peux utiliser un thread qui appelle la fonction système qui "tue" les processus. Mais je ne sais pas si un processus peut s'auto-tuer, à essayer...
Sat83
Messages postés166Date d'inscriptionmardi 11 novembre 2003StatutMembreDernière intervention13 octobre 2008 21 avril 2008 à 19:23
Pour l'instant j'en suis a la solution avec le Timer.
J'envisage de passer a la solution avec un second thread, mais seulement si c'est utile. C'est pour cette raison que j'ai preféré poser la question avant de me lancer dans un developpement inutile qui m'aurait amené au même resultat.
Dans ce que j'imaginais comme solution, dans mon second thread je voulais soit fermer le programme normalement si c'est possible, soit killer le processus (en passant par un .bat intermediaire).
Sat83
Messages postés166Date d'inscriptionmardi 11 novembre 2003StatutMembreDernière intervention13 octobre 2008 22 avril 2008 à 08:46
Merci pour ta solution!
Y'aurait-il un moyen de tenter de fermer le programme "normalement" dans le thread, de detecter si c'est pas possible, et dans ce cas là seulement forcer la fermeture avec TerminateProcess ?
Rematrix
Messages postés115Date d'inscriptionjeudi 17 avril 2008StatutMembreDernière intervention16 juillet 2008 22 avril 2008 à 12:29
Si j'été vous j'ivetrais de faire un terminateprocess, fermer comme ça brutement c pas trés propre éssez plus tôt d'envoyé un sms de type WM_CLOSE ça feras la faire genre SendMessage(fProcessHndl , WM_CLOSE, 0, 0);.
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 22 avril 2008 à 12:54
Bon alors d'après Cari, le processus peut d'auto tuer
Mais TerminateProcess() libère quand même toutes les ressources (et oui !) ***
Ce qu'il ne fait pas, c'est exécuter les procédures de "finalization", les OnDestroy(), etc...
Donc c'est pas propre si par exemple le programme veut pouvoir sauvegarder son fichier de configuration.
Donc, comme le demande Sat83, on peut ruser :
- Une première demande de fermeture "propre": le sms WM_CLOSE fonctionne parfaitement sur toutes les applications VCL Delphi (ce n'est pas forcément le cas pour les autres langages)
- Si au bout de... disons 5 secondes, le programme ne s'est toujours pas arrêté, on fait appel à TerminateProcess
*** : Quoi que j'ai tout de même un doute pour les fichiers ouverts.
Rematrix
Messages postés115Date d'inscriptionjeudi 17 avril 2008StatutMembreDernière intervention16 juillet 2008 22 avril 2008 à 14:14
presque parfait , code extras il te manque je pense juste un callback pour utiliser EnumThreadWindows et pour que ça soit un serialkillerEnumThreadWindows(dwGuiThreadId, EnumProc, 0);
Matrix
Caribensila
Messages postés2527Date d'inscriptionjeudi 15 janvier 2004StatutMembreDernière intervention16 octobre 201918 22 avril 2008 à 15:32
Ultra-chipotage :
Je ne crois pas que WaitForSingleObject consomme beaucoup en ressources processeur, mais on pourrait peut-être quand même mettre la priorité du Thread à tpLowest :
Rematrix
Messages postés115Date d'inscriptionjeudi 17 avril 2008StatutMembreDernière intervention16 juillet 2008 23 avril 2008 à 11:10
@[auteur/FLORENTH/316333.aspx florenth] j'ai juste dit si tu utilise WM_CLOSE ben! ça marche pas pour tous les applications, par exemple si tu envoi un sms ou mms de type wm_close au blocnote poar exemple il vas te dire "le texte du fichier balbla a été blilbli voulez vous ...oui - non" tu voi ce que je veux dire, et aussi par ce que aussi tu sais bien que ce message ne fait que détruire une fenêtre mais pas les threads liée au application, alors si tu veux faire un meurtre professionnel sans terminateproccess ben! il faut nétoyé tous le secteur,.
florenth
Messages postés1023Date d'inscriptiondimanche 1 août 2004StatutMembreDernière intervention17 août 20083 23 avril 2008 à 11:45
@Rematrix: en effet, mais j'ai bien précisé dans mon post que cela n'étais valable que si tu avais une application Delphi VCL.
Dans le cas d'une app VCL, lorsque le FormClose s'exécute sur la fiche principale et valide la fermeture (note qu'il peut aussi ne pas valider la fermeture via un messagedlg si tu le souhaites) alors Delphi va:
- fermer la fenêtre
- laisser tous les gestionnaires d'évènements se finir
- quitter l'application.
Donc dans ce cas c'est suffisant.
Sinon, tu peux toujours te créer un message personnalisé qui évite de faire appel à la boîte de confirmation, si jamais tu es dans ce cas-là.
Rematrix
Messages postés115Date d'inscriptionjeudi 17 avril 2008StatutMembreDernière intervention16 juillet 2008 23 avril 2008 à 12:09
tu parlé peut etre de wm_endsession ou de wm_queryendesession , pour moi j'ai éssayé juste de généralisé et j'ai pas fait attention a ton poste par ce que j'ai jamais du temps libre alors j'éssay de participé comme même, mais c cool de ton on temps de recontré des gens qui s'interesse a des applications de type C.
Matrix