J'ai découvert que si une méthode en file d'attente avec [EDIT: En fait, ils commencent dans l'ordre de FIFO. Avec J'essaie de vous assurer que toutes les méthodes en file d'attente du thread de travailleur à exécuter dans le fil VCL exécuté dans une commande de FIFO stricte et qu'ils sont tous terminés avant le fil de travail est détruit. P> Mon autre contrainte est que j'essaie de maintenir une séparation stricte de l'interface graphique de la logique commerciale. Le fil dans ce cas fait partie de la couche logique de l'entreprise afin que je ne puisse pas utiliser Voici comment ma méthode de Tthread.execute se termine: P> tthread.queue appelle code> appelle une méthode qui invoque tapsplication.wndproc code> (par exemple, showMessage code>) puis suivant Les méthodes en file d'attente sont autorisées à exécuter avant la fin de la méthode d'origine. Pire encore, ils ne semblent pas être invoqués dans l'ordre de FIFO. ShowMessage CODE> On dirait que le plus tard, on a couru d'abord parce qu'il y a un appel à checksynchroniser code> avant l'apparition de la boîte de dialogue. Cela incombe à la méthode suivante et l'exécute, ne revenant pas avant la fin de la dernière méthode. Alors seulement, la boîte de dialogue apparaît.] P> postmessage code> à partir d'un handliner code> pour organiser pour que le fil soit détruit (comme recommandé par un Nombre de contributeurs ailleurs). Donc, je règle frongonterminate: = true code> dans une méthode finale en file d'attente juste avant de tthread.execute sorties. (D'où la nécessité d'être exécutée dans un ordre de FIFO strict.) P> finally
// Queue a final method to execute in the main thread that will set an event
// allowing this thread to exit. This ensures that this thread can't exit
// until all of the queued procedures have run.
Queue(
procedure
begin
if Assigned(fOnComplete) then
begin
fOnComplete(Self);
// Handler sets fWorker.FreeOnTerminate := True and fWorker := nil
end;
SetEvent(fCanExit);
end);
WaitForSingleObject(fCanExit, INFINITE);
end;
4 Réponses :
Si vous souhaitez vous assurer que une méthode de file d'attente est appelée avant la terminaison du three, vous devez utiliser Réglage tthread.Queue () code> est une file d'attente FIFO. En fait, il partage la même file d'attente que thread.sychronise () code> utilise. Mais vous êtes correct que la manutention du message provoque l'exécution des méthodes en file d'attente. Ceci est parce que tapplication.idle () code> appels checksynchroniser () code> Chaque fois que la file d'attente de messages passe au ralenti après le traitement de nouveaux messages. Donc, si une méthode en file d'attente / synchronisée invoque le traitement des messages, cela peut permettre d'exécuter d'autres méthodes en file d'attente / synchronisées, même si la méthode précédente est toujours en cours d'exécution. P>
synchroniser () code> au lieu de la file d'attente () code> ou utilisez le Onterminate code> événement à la place (qui est déclenché par synchroniser () code>). Ce que vous faites dans votre enfin code> Le bloc est effectivement identique à celui de l'événement ONIMINER CODE> NATIVE. P>
Freonterminate: = true code> dans une méthode em> mise en file d'attente est de demander une fuite de mémoire. Freonterminate CODE> est évalué immédiatement lorsque Exécuter () CODE> Sorties, avant DoMerminate () Code> est appelé pour déclencher l'événement code> (code> Ce qui est un superposition à mon avis, comme l'évaluait que précoce empêche à l'onduleur code> de décider au moment de la résiliation si un thread devrait se libérer ou non après à l'onduleur code> sorties). Donc, si la méthode en file d'attente fonctionne après Execute () code> a quitté, il n'y a aucune garantie que Freonterminate code> sera défini à temps. En attente d'une méthode em> mise en attente em> pour terminer avant de renvoyer le contrôle sur le thread est exactement ce que synchroniser () code> est destiné à. synchroniser () code> est synchrone, il attend que la méthode de sortie. file d'attente () code> est asynchrone, il n'attend pas du tout. p>
J'ai lu votre autre réponse suggérant appelant à l'heure de synchronisation avant d'exécuter des sorties. Quand j'ai essayé cela, j'ai trouvé que l'on a semblé à exécuter avant i> les méthodes de la file d'attente () D, donc je supposais qu'il a sauté la file d'attente. Maintenant, je sais que c'était parce que les méthodes en file d'attente ont appelé la manipulation de messages et ce que j'ai écrit avec le serviforSingleObject est fonctionnellement équivalent à la synchronisation. Mais je suis toujours laissé avec le problème initial, comment obtenir le fil se libérer de lui-même, mais seulement après toutes les méthodes en file d'attente terminées, lorsque les méthodes en file d'attente pourraient elles-mêmes invoquer la manipulation des messages.
C'est la solution que j'ai enfin adoptée.
J'ai utilisé un Delphi TCountDownEvent code> pour suivre le nombre de méthodes en file d'attente exceptionnelles à partir de mon thread, incrémentation du nombre de comptes juste avant de faire la queue d'une méthode et en la décrétant comme l'acte final de la méthode en file d'attente. P>
juste avant ma substitution de Une fois que toutes les méthodes en file d'attente sont complètes, il appelle Le seul Wrinkle est que Normalement, cela serait risqué, mais dans mon cas, nous savons qu'au moment du thread commence à attendre le nombre de méthodes en file d'attente en circulation pour atteindre zéro aucune autre méthode ne peut être mise en file d'attente (donc à partir de ce point une fois que le comte frappe zéro, il restera à zéro). P> Ceci ne complètement < / EM> résolvez le problème des méthodes en file d'attente qui gérent les messages, dans lesquelles des méthodes en file d'attente individuelles pourraient toujours être à l'origine de l'ordre. Mais je dois maintenant avoir la garantie que toutes les méthodes en file d'attente fonctionnent de manière asynchrone mais seront complétées avant la sortie du fil. C'était l'objectif principal, car il permet au fil de se nettoyer sans risque de perdre des méthodes en file d'attente. P> P> tthread.execute code> renvoie, il attend que le TCountDownEvent code> objet à signaler, c'est-à-dire lorsque le nombre atteint zéro. C'est l'étape cruciale qui garantit que toutes les méthodes en file d'attente ont terminé avant exécuter code> retours. P> synchroniser Code> avec un OnComplete Code> Handler - Grâce à Rémy pour indiquer que cela est équivalent à celui que mon code d'origine utilisait file d'attente code> et waitforsingleObject code> . ( OnComplete code> est comme à l'ondulate code>, mais appelé Avant d'exécuter les retours de sorte que le gestionnaire puisse modifier Freonterminate code>.) p> tcountdowntevent.addcount code> ne fonctionne que si le nombre est déjà supérieur à zéro. Donc j'ai écrit une aide de classe à implémenter forceaddcount code>: p>
Quelques réflexions: P>
J'ai corrigé ce problème en ajoutant un appel à synchroniser () code> à la fin de mon exécuté () code> méthode. Cela oblige le fil à attendre jusqu'à ce que tous les appels ajoutés avec la file d'attente () code> sont terminés sur le fil principal avant l'appel ajouté avec synchroniser () code> peut être appelé. TMyThread = class (TThread)
private
procedure QueueMethod;
procedure DummySync;
protected
procedure Execute; override;
end;
procedure TMyThread.QueueMethod;
begin
// Do something on the main thread
UpdateSomething;
end;
procedure TMyThread.DummySync;
begin
// You don't need to do anything here. It's just used
// as a fence to stop the thread ending before all the
// Queued messages are processed.
end;
procedure TMyThread.Execute;
begin
while SomeCondition do
begin
// Some process
Queue(QueueMethod);
end;
Synchronize(DummySync);
end;
La solution la plus simple jamais.
Semble que la file d'attente n'est pas le bon choix ici. Vous pouvez envisager d'utiliser la synchronisation à la place.
@Uweraabe J'aurais dû dire que les méthodes en file d'attente doivent être exécutées de manière asynchrone au fil du travailleur. Le thread du travailleur ne peut attendre qu'une fois qu'il a terminé tout son travail et qu'il est prêt à sortir.
IMHO L'exigence selon laquelle
toutes les méthodes en file d'attente s'est terminée avant que le thread du travailleur soit libéré code> indique un couplage (trop) fort entre ce fil et les méthodes en file d'attente, qui sont exécutées dans le contexte d'un fil différent@MJN Ceci se pose car il existe une exigence que le thread principal peut terminer le fil de travailleur à tout moment. Mais étant donné que le fil principal ne peut pas utiliser la technique postmessage habituelle pour libérer le fil de travailleur à partir de son gestionnaire à l'onduleur, nous devons utiliser Freonterminate: = True, mais cela ne peut pas être défini avant que toutes les méthodes en fileté soient terminées, car toutes les méthodes toujours dans la file d'attente Lorsque le fil est détruit, se détruit avec le fil. Mais je suis ouvert à d'autres moyens d'atteindre mes objectifs. Toutes les suggestions sont les bienvenues.
@mjm en fait, je pourrait i> utiliser PostMessage pour libérer le fil si le fil avait son propre WNDProc; Il est sans doute le seul moyen fiable de le faire de toute façon. Mais si j'utilise Windows Messaging pour cela, je pourrais aussi bien aller tout le porc et utiliser la messagerie de Windows au lieu de tthread.queue partout. Venez à cela, je pourrais aussi bien utiliser l'API Win32 natif plutôt que Ththread du tout. Mais je cherche la solution la plus simple qui fonctionne et cela semble stupide de ne pas exploiter le VCL.
@Iangoldby Avez-vous trouvé une meilleure solution pour cela? Je suis très intéressé par ça.
@ user15124 Voir la réponse acceptée ci-dessous. C'est la solution que j'ai adoptée.
Vous pouvez avoir un compteur dans chaque thread sur le nombre de messages où l'envoi de ce fil et attendez-vous 0 lorsque le thread quitte.
si Freonterminate et fcounter = 0 puis quittez; code> qui nécessiterait un système de fil et de messagerie personnalisé cependant.@ user15124 Je ne vois pas comment cela est différent de la solution que j'ai écrit, sauf que, avec votre suggestion sans objet de synchronisation, vous auriez à sonder jusqu'à ce que FCounter soit devenu zéro. L'utilisation d'un TcountDownEvent comme décrit évite ce problème.