Classe de thread à Delphi - (1)
Classe de threads à Delphi - (1) Raptor (travail original)
Mot-clé threadEventCriticalSectionSynchronize
Classes de threads à Delphi
Raptors [MentalStudio]
http://mental.mentsu.com
(un)
Il y a une classe de threads à Delphi qui est utilisée pour implémenter la programmation multi-thread. La synchronisation est terminée. Cependant, ce n'est pas l'ensemble de la programmation multi-thread.
Un fil est essentiellement un morceau de code exécutant simultanément dans un processus. Un processus a au moins un thread, le soi-disant thread principal. Il peut également y avoir plusieurs fils d'enfants. Lorsque plus d'un thread est utilisé dans un processus, il est appelé "multi-threading".
Alors, comment ce soi-disant "un morceau de code" est-il défini? C'est en fait une fonction ou un processus (pour Delphi).
Si vous utilisez l'API Windows pour créer des threads, il est implémenté via une fonction API appelée CreateTheRad, qui est définie comme:
HandlereateThread (
LPsecurity_attributeslpthreadattributes,
Dworddwstacksize,
Lpthread_start_routinelpstartaddress,
Lpvoidlpparamètre,
Dworddwcreationflags,
LPDWORDLPTHREADID
));
Leurs paramètres sont comme mentionnés dans leurs noms, à savoir: les attributs de threads (utilisés pour définir des attributs de sécurité de threads sous NT, invalides sous 9x), la taille de la pile, l'adresse de démarrage, les paramètres et les drapeaux de création (utilisés pour définir les threads l'état au moment de la création) , ID de filetage, et enfin renvoyer la poignée du fil. L'adresse de départ est l'entrée de la fonction de thread et le thread se termine jusqu'à ce que la fonction de thread se termine.
Le processus d'exécution de l'ensemble du thread est le suivant:
Parce que les paramètres de création sont nombreux et c'est une API Windows, une fonction de threadage générale est fournie dans CruntimeLibrary (en théorie, il peut être utilisé dans n'importe quel système d'exploitation qui prend en charge les threads):
unsignedlong_beginthread (void (_USERENTRY * __ start) (void *), unsigned__stkSize, void * __ arg);
Delphi fournit également une fonction similaire avec la même fonction:
functionBeginThread (SecurityAttributes: Pointer; StackSize: Longword; ThreadFunc: TTHREADFUNC; Paramètre: Pointer; CreationFlags: Longword; VarThaRedId: Longword): entier;
Les fonctions de ces trois fonctions sont fondamentalement les mêmes. La plus grande différence entre les fonctions de thread et les fonctions générales est que dès le démarrage de la fonction de thread, ces trois fonctions de threads reviennent et que le thread principal continue d'exécuter vers le bas, tandis que la fonction de thread est exécutée dans un thread indépendant. Il faut pour exécuter et quoi lors du retour, le fil principal ne s'en soucie pas ou ne le sait pas.
Dans des circonstances normales, après le retour de la fonction de thread, le thread se termine. Mais il y a d'autres façons:
API Windows:
VoidexitThread (dworddwexitcode);
Cruntimélibrary:
void_endthread (void);
DelphiruntimeLibrary:
ProcédureEndThread (exitcode: entier);
Afin d'enregistrer certaines données de threads nécessaires (état / propriétés, etc.), le système d'exploitation créera un objet interne pour le thread. Le fil se termine.
Bien que la programmation multi-threading puisse être facilement effectuée à l'aide de l'API ou du RTL (RuntimeBrary), un traitement plus détaillé est toujours requis.
Il est également très simple d'utiliser cette classe. Il s'agit de la fonction de thread, qui est la pièce de code exécutée dans le thread). Pour plus de détails à ce sujet, je ne les répéterai pas ici, veuillez vous référer aux livres pertinents.
Ensuite, dans cet article, nous discuterons de la façon dont la classe TTHREAD résume les threads, c'est-à-dire que nous effectuerons des recherches approfondies sur la mise en œuvre de la classe TTHEAD. Parce qu'il est préférable de l'utiliser si vous le comprenez vraiment.
Vous trouverez ci-dessous la déclaration de la classe TTHREAD dans Delphi7 (cet article ne traite que de l'implémentation sous la plate-forme Windows, donc tout le code sur la pièce Linux Platform est supprimé):
TThread = classe
Privé
Fhandle: Thandle;
Fthreadid: thandle;
FREATESE SUPPENDÉ: Boolean;
Fterminé: booléen;
FSUSSPENDED: Boolean;
Ffreeonterniate: booléen;
Ffinished: booléen;
FreTurnValue: entier;
Fonterminate: tnotifyEvent;
Fsynchronize: tsynchronizercord;
Ffatalexception: tobject;
procédurecallonterminé;
classProceDereSynchronize (asynCrec: psynchronizercord);
FonctionGetPriority: TTHREADPRIORITY;
ProcédureSetpriority (valeur: tThreadpriority);
Procédures SURSPED (valeur: booléen);
protégé
procédureCheckThreaReRor (errCode: Integer); surcharge;
ProcedureCheckThrEReRorror (Success: Boolean); surcharge;
procédurotermiate; virtuel;
Procédure Execute; virtuel; résumé;
ProcédureSynchronize (Méthode: tThreadMethod); surcharge;
PropertyReturnValue: IntegerReadFreTurnValuewriteFreTurnValue;
Propriété de propriété: booleanreadfterminé;
publique
ConstructorCreate (Créer, Boolean);
destructordestroy; remplacer;
procédure après la construction; remplacer;
procédure
procédures à utiliser;
procédurettermiate;
functionwaitfor: Longword;
ClassProcedEredReynChronize (Athread: Tthread; Amethod: tThreadMethod); surcharge;
ClassProcedUrestaticSynChronize (Athread: Tthread; amethod: tThreadMethod);
PropertyFatAlexception: TobjectaDffatalexception;
PropertyFreeOnterniminé: booleanreadffreeonterniatewriteffreeonterminiate;
PropertyHandle: Thandlereadfhandle;
PropertyPriority: TTHREADPRIORITYREADGETPRIORITYWRITESETPRIORITY;
Propriété AsUSPEND: BooleanreadfSusPededWriteSetSpended;
PropertyThreadId: ThandlereadfThreaDID;
PropertyOnterminiate: tnotifyEventReadfonterniminatewriteFonternimine;
fin;
La classe TTHREAD est une classe relativement simple dans Delphi RTL, avec peu de membres de la classe et les attributs de classe sont très simples et clairs.
(à suivre)
Classe de threads à Delphi - (2)
Classe de threads à Delphi - (2) Raptor (travail original)
Mot-clé threadEventCriticalSectionSynchronize
Classes de threads à Delphi
Raptors [MentalStudio]
http://mental.mentsu.com
Deux
Le premier est le constructeur:
ConstructOrtThread.Create (CreateSpened: Boolean);
Commencer
Hérité de création;
AddThread;
FSUSPEnd: = CreateSpeded;
FcreateSUSPEnd: = CreateSpened;
Fhandle: = BeginThread (nil, 0, @ threadProc, pointer (self), create_suspeded, fthreaDID);
iffhandle = 0Then
roseEthread.creareesfmt (@StHreadCreateError, [syserrorMessage (getlasterRor)]);
fin;
Bien que ce constructeur n'ait pas beaucoup de code, il peut être considéré comme le membre le plus important car le thread est créé ici.
Après avoir appelé TOBject.Create à travers hérité, la première phrase consiste à appeler un processus: AddThread, son code source est le suivant:
ProcédureAddThread;
Commencer
InterlockedIncrement (ThreadCount);
fin;
Il y a aussi une suppression correspondante:
procédureRovethread;
Commencer
InterlockedDecment (ThreadCount);
fin;
Leur fonction est très simple, ce qui est de compter le nombre de threads dans le processus en ajoutant ou en diminuant une variable globale. Cependant, le processus Inc / DEC couramment utilisé ici n'est pas le processus Inc / DEC couramment utilisé, mais le processus interlockédIncrement / interlockedDecment est utilisé. Mais il y a une plus grande différence entre eux, c'est-à-dire que InterlockedInCment / interlockedDecment est enfilant. Autrement dit, ils peuvent s'assurer que les résultats d'exécution sont corrects sous le multithreading, mais Inc / DEC ne le peut pas. Ou en termes de théorie du système d'exploitation, il s'agit d'une paire d'opérations "primitives".
Prenez l'addition comme exemple pour illustrer la différence de détails de mise en œuvre des deux:
D'une manière générale, il y a trois étapes après la décomposition du fonctionnement de l'ajout d'une aux données de mémoire:
1. Lire les données de la mémoire
2. Ajouter une données
3. Enregistrez-le en mémoire
Supposons maintenant qu'une opération d'ajouter une utilisant Inc dans une application à deux threads puisse se produire:
1. Filetant A lit les données de la mémoire (en supposant qu'elle est 3)
2. Le thread B lit les données de la mémoire (également 3)
3. Le thread A ajoute un aux données (maintenant c'est 4)
4. Thread B en ajoute un aux données (maintenant il est également 4)
5. Fixez les données de stockage dans la mémoire (les données en mémoire sont maintenant 4)
6. Le thread B stocke également les données en mémoire (les données en mémoire sont encore 4 maintenant, mais les deux threads en ajoutent un, qui devraient être 5, donc un résultat d'erreur apparaît ici)
Il n'y a aucun problème avec le processus interlockincrement, car le soi-disant "primitif" est une opération sans interruption, c'est-à-dire que le système d'exploitation peut garantir qu'aucune commutation de thread ne sera effectuée avant l'exécution d'un "primitif". Ainsi, dans l'exemple ci-dessus, ce n'est qu'après que le thread A a terminé le stockage des données dans la mémoire, le thread B peut commencer à récupérer le nombre à partir de celui-ci et à ajouter une opération, ce qui garantit que même dans des situations multiples, le résultat sera certainement correct.
L'exemple précédent illustre également une situation de «conflit d'accès aux fils», c'est pourquoi les threads ont besoin de «synchronisation».
En parlant de synchronisation, il y a une distraction: Li Ming, professeur à l'Université de Waterloo au Canada, a fait relever des objections à la traduction du mot synchronisé comme la "synchronisation" dans la "synchronisation des fils". très raisonnable. En chinois, la «synchronisation» signifie «se produire en même temps», tandis que la «synchronisation des threads» est destinée à éviter une telle «se produire en même temps». En anglais, la synchronisation a deux significations: l'une est la synchronisation au sens traditionnel (TOOCCURATTHESAMETIME), et l'autre est la "coordination". Le mot synchronisé dans la "synchronisation du thread" doit se référer à ce dernier sens, c'est-à-dire "pour s'assurer que plusieurs threads maintiennent la coordination et évitent les erreurs lors de l'accès aux mêmes données." Cependant, il y a encore beaucoup de mots qui ne sont pas traduits avec précision dans l'industrie informatique. il doit être clarifié.
Je vais aller loin, et je reviendrai au constructeur de Tthread.
Fhandle: = BeginThread (nil, 0, @ threadProc, pointer (self), create_suspeded, fthreaDID);
Ici, nous utilisons la fonction delphirtl Beginthread mentionnée ci-dessus. Le troisième paramètre est la fonction de thread mentionnée ci-dessus, c'est-à-dire la pièce de code exécutée dans le thread. Le quatrième paramètre est le paramètre transmis à la fonction de thread, voici l'objet de thread créé (c'est-à-dire auto). Entre autres paramètres, le cinquième est utilisé pour définir le thread à suspendre après la création et ne pas s'exécuter immédiatement (le travail de démarrage du thread est déterminé en après-construction en fonction du drapeau de CreateSpeded), et le sixième est de retourner l'ID de fil.
Regardons maintenant le cœur de TTHREAD: la fonction de thread ThreadProc. Fait intéressant, le noyau de cette classe de thread n'est pas un membre du thread, mais une fonction globale (car la convention des paramètres du processus BeginThread ne peut utiliser que des fonctions globales). Voici son code:
FunctionThreadProc (Thread: Tthread): entier;
var
Freethread: Boolean;
Commencer
essayer
ifnotthread.
essayer
Thread.execcute;
sauf
Thread.ffatalexception: = acquireExceptionObject;
fin;
Enfin
Freethread: = thread.ffreeonterniate;
Résultat: = thread.freTurnValue;
Thread.dotermiate;
Thread.ffinished: = true;
SignalSyncevent;
iffreethreadthenthread.free;
EndThread (résultat);
fin;
fin;
Bien qu'il n'y ait pas beaucoup de code, c'est la partie la plus importante de l'ensemble du TTHREAD car ce code est un code qui est réellement exécuté dans un thread. Ce qui suit est une explication ligne par ligne du code:
Tout d'abord, jugez le drapeau terminé de la classe de threads. exécute essentiellement le code d'exécution dans la classe dérivée.
Par conséquent, l'exécution est une fonction de thread dans une classe de threads.
Si une exception se produit dans EXECUTE, l'objet d'exception est obtenu via AcquireExceptionObject et stocké dans le membre ffatalexception de la classe de threads.
Enfin, il y a des travaux de finition effectués avant la fin du fil. La variable locale Freethread enregistre le paramètre de la propriété FreeOnTermimiée de la classe de threads, puis définit la valeur de retour de thread à la valeur de l'attribut de valeur de retour de classe de thread. Exécutez ensuite la méthode Dotermiate de la classe de threads.
Le code de la méthode Dotermiate est le suivant:
procéduretthread.dotermiate;
Commencer
ifassigned (fonterniminer) thensynchronize (callonterniminer);
fin;
C'est très simple, il s'agit d'appeler la méthode CallonTermiate par Synchronize, et le code de la méthode CallonTermiate est le suivant, qui est simplement d'appeler l'événement Onterminate:
procéduretthread.Callonterminiate;
Commencer
ifassigned (fonter) thenfonterniate (self);
fin;
Étant donné que l'événement Onterminate est exécuté en synchronisé, il n'est essentiellement pas du code de thread, mais du code de thread principal (voir l'analyse de la synchronisation plus tard).
Après avoir exécuté ontermiate, définissez le drapeau ffinished de la classe de threads à TRUE.
Ensuite, exécutez le processus SignalSyNCevent et son code est le suivant:
ProcéduresignalSyncevent;
Commencer
SetEvent (syncevent);
fin;
Il est également très simple, il s'agit de mettre en place un événement mondial: Sycevent.