Clase de hilo en Delphi-(1)
Clase de hilo en Delphi-(1) Raptor (trabajo original)
Palabra clave ThreadVentCriticalSectionSectionSynCronize
Clases de hilo en Delphi
Raptors [MentalStudio]
http://mental.mentsu.com
(uno)
Hay una clase Tthread de hilo en Delphi que se utiliza para implementar la programación de múltiples hilos. Sincronize están terminados. Sin embargo, esta no es toda la programación múltiple.
Un hilo es esencialmente un código que se ejecuta simultáneamente en un proceso. Un proceso tiene al menos un hilo, el llamado hilo principal. También puede haber múltiples hilos infantiles. Cuando se usa más de un hilo en un proceso, se llama "múltiples subprocesos".
Entonces, ¿cómo se define este llamado "una pieza de código"? En realidad es una función o proceso (para Delphi).
Si usa la API de Windows para crear hilos, se implementa a través de una función API llamada CreateThread, que se define como:
HandLecreatethread (
Lpsecurity_attributeslpThreadattributes,
DWordDWStackSize,
Lpthread_start_routinelpstartaddress,
Lpvoidlparameter,
DWordDWCreationFlags,
Lpdwordlpthreadid
);
Sus parámetros son como se mencionan en sus nombres, a saber: los atributos de hilo (utilizados para establecer atributos de seguridad de subprocesos en NT, inválido bajo 9x), tamaño de pila, dirección inicial, parámetros y banderas de creación (se usa para establecer los subprocesos en el estado de creación) , ID de subproceso y finalmente devuelve el mango del hilo. La dirección inicial es la entrada a la función de subproceso, y el hilo termina hasta que termina la función de subproceso.
El proceso de ejecución de todo el hilo es el siguiente:
Debido a que los parámetros de creación son muchos y es una API de Windows, se proporciona una función de subproceso general en Cruntimelibrary (en teoría, se puede usar en cualquier sistema operativo que admita los subprocesos):
unsignedlong_beginthread (void (_userEntry*__ inicio) (void*), unsigned__stksize, void*__ arg);
Delphi también proporciona una función similar con la misma función:
functionBeginthread (SecurityAttributes: Pointer; StackSize: Longword; Threadfunc: tthreadfunc; parámetro: Pointer; CreationFlags: Longword; Varthreadid: Longword): Integer;
Las funciones de estas tres funciones son básicamente las mismas. La mayor diferencia entre las funciones de hilo y las funciones generales es que tan pronto como se inicia la función de subproceso, estas tres hilos comienzan las funciones, y el hilo principal continúa ejecutándose hacia abajo, mientras que la función de hilo se ejecuta en un hilo independiente. Se necesita para ejecutar y qué al regresar, el hilo principal no le importa ni lo sabe.
En circunstancias normales, después de que la función de subproceso regresa, el hilo termina. Pero hay otras formas:
API de Windows:
VoidexitThread (dWordDWexitCode);
Cruntimelibrary:
void_endthread (void);
Delphiruntimelibrary:
ProcedureEntThread (ExitCode: Integer);
Para registrar algunos datos de subprocesos necesarios (estado/propiedades, etc.), el sistema operativo creará un objeto interno para el hilo. El hilo termina.
Aunque la programación de múltiples subprocesos se puede realizar fácilmente utilizando API o RTL (Runtimelibrary), todavía se requiere un procesamiento más detallado.
También es muy simple usar esta clase. Esta es la función de subproceso, que es la parte del código ejecutada en el hilo). Para obtener detalles específicos sobre esto, no los repetiré aquí, consulte los libros relevantes.
A continuación, en este artículo, discutiremos cómo la clase Tthread encapsula los hilos, es decir, realizaremos una investigación en profundidad sobre la implementación de la clase TTHREAD. Porque solo es mejor usarlo si realmente lo entiendes.
A continuación se muestra la declaración de la clase Tthread en Delphi7 (este artículo solo discute la implementación en la plataforma Windows, por lo que se elimina todo el código sobre la parte de la plataforma Linux):
Tthread = clase
Privado
Fhandle: Thandle;
Fthreadid: Thandle;
FCreateSuspended: booleano;
Ferminado: booleano;
Fsuspended: booleano;
FfreeonterMinate: booleano;
FINISIDO: booleano;
Freturnvalue: entero;
FonterMinate: tnotifyEvent;
FsynChronize: tsynchronizerecord;
Ffatalexception: tobject;
procedimientoCallon terminado;
classprocedureSynChronize (asyncrec: psynchronizerecord);
functetPriority: tthreadpriority;
ProcedurSetPriority (valor: tthreadpriority);
procedimientoSetsUspended (valor: booleano);
protegido
procedimientoCheckThreadError (errcode: integer); sobrecarga;
procedimientoCheckThreadError (éxito: boolean); sobrecarga;
procedido nominado; virtual;
ProcedureExCute; Virtual; Abstract;
procedimientosyncronize (método: tthreadMethod); sobrecarga;
PropertyReturnValue: IntegerReadfreturnValueWritefreturnValue;
Terminado por la propiedad: booleanReadFermined;
público
constructorCreate (createSoSpended: boolean);
Destructordestroy; anular;
Procedimiento de la construcción; anulación;
Procedimiento de suma;
procedimientos utilizar;
procedimiento
functionWaitfor: Longword;
classprocedureSynchronize (Athread: tthread; amethod: tthreadMethod); sobrecarga;
classprocedureStaticsynchronize (Athread: tthread; amethod: tthreadMethod);
PropertyFatalException: tobjectreadffatalException;
PropertyFreeonterMinate: booleanreadffreeonterminatewriteffreeonterminate;
PropertyHandle: Thandlereadfhandle;
PropertyPriority: tthreadpriorityReadgetPriorityWritesetPriority;
PropertySuspended: booleanreadfsuspendedwriteSetSuspended;
PropertyThreadID: Thandleraradfthreadid;
PropertyOnterMinate: tnotifyEventReadfonterMinateWriteFonterMinate;
fin;
La clase Tthread es una clase relativamente simple en Delphi RTL, con no muchos miembros de la clase y los atributos de la clase son muy simples y claros.
(continuará)
Clase de hilo en Delphi-(2)
Clase de hilo en Delphi-(2) Raptor (trabajo original)
Palabra clave ThreadVentCriticalSectionSectionSynCronize
Clases de hilo en Delphi
Raptors [MentalStudio]
http://mental.mentsu.com
Dos
El primero es el constructor:
constructortthread.create (createSoSpended: boolean);
Comenzar
HerededCreate;
AddThread;
Fsuspended: = createSuspended;
FCreateSuspended: = CreateSuspended;
Fhandle: = beginthread (nil, 0,@threadproc, pointer (self), create_suspended, fthreadid);
iffhandle = 0then
RauseThread.Createresfmt (@SthreadCreateError, [syserrormessage (getLasterRor)]);
fin;
Aunque este constructor no tiene mucho código, puede considerarse como el miembro más importante porque el hilo se crea aquí.
Después de llamar a Tobject.Create a través de Hereited, la primera oración es llamar a un proceso: AddThread, su código fuente es el siguiente:
procedimientoaddthread;
Comenzar
InterlockedIncrement (threadcount);
fin;
También hay un RemoVethread correspondiente:
procedimientoMoVethread;
Comenzar
InterlockedDecrement (threadcount);
fin;
Su función es muy simple, lo que es contar el número de hilos en el proceso agregando o disminuyendo una variable global. Sin embargo, el proceso INC/DEC comúnmente utilizado aquí no es el proceso Inc/Dec comúnmente utilizado, pero el proceso de entrada de InterlockedRement/InterlockedDecrement se utiliza. Pero hay una mayor diferencia entre ellos, es decir, InterlockedIncrement/InterlockedDecrement es seguro de hilo. Es decir, pueden asegurarse de que los resultados de la ejecución sean correctos en Multithreading, pero Inc/Dec no puede. O en términos de teoría del sistema operativo, este es un par de operaciones "primitivas".
Tome el adicional como ejemplo para ilustrar la diferencia en los detalles de implementación de los dos:
En términos generales, hay tres pasos después de descomponer la operación de agregar uno a los datos de memoria:
1. Lea los datos de la memoria
2. Agregue un datos
3. Guárdelo en la memoria
Ahora suponga que puede ocurrir una operación de agregar una usando Inc en una aplicación de dos hilos:
1. Hilo A lee datos de la memoria (suponiendo que sea 3)
2. El hilo B lee datos de la memoria (también 3)
3. Hilo A agrega uno a los datos (ahora son 4)
4. El hilo B agrega uno a los datos (ahora también es 4)
5. Enhebre un almacena de datos en la memoria (los datos en la memoria ahora son 4)
6. El hilo B también almacena datos en la memoria (los datos en la memoria aún son 4 ahora, pero ambos subprocesos le agregan uno, que debería ser 5, por lo que aparece un resultado de error aquí)
No hay ningún problema con el proceso de InterlockIncrement, porque la llamada "primitiva" es una operación ininterrumpida, es decir, el sistema operativo puede garantizar que no se realice una conmutación de roscado antes de ejecutar un "primitivo". Entonces, en el ejemplo anterior, solo después de que el subproceso A haya terminado de almacenar datos en la memoria, el subproceso B puede comenzar a obtener el número y agregar una operación, lo que asegura que incluso en situaciones de múltiples subprocesos, el resultado definitivamente será correcto.
El ejemplo anterior también ilustra una situación de "conflicto de acceso a hilos", por lo que los hilos necesitan "sincronizar".
Hablando de sincronización, hay una distracción: Li Ming, profesor de la Universidad de Waterloo en Canadá, una vez planteó objeciones a la traducción de la palabra sincronización como "sincronización" en "sincronización de hilos". muy razonable. En chino, la "sincronización" significa "ocurrir al mismo tiempo", mientras que la "sincronización de hilos" tiene la intención de evitar tal "ocurrir al mismo tiempo". En inglés, Synchronize tiene dos significados: uno es la sincronización en el sentido tradicional (OccuratTheSametime), y el otro es "coordinación". La palabra sincronización en "sincronización de hilos" debe referirse al último significado, es decir, "para garantizar que múltiples hilos mantengan la coordinación y eviten errores al acceder a los mismos datos". Sin embargo, todavía hay muchas palabras como esta que no se traducen con precisión en la industria de TI. Debe aclararse.
Voy a ir lejos y volveré al constructor de Tthread.
Fhandle: = beginthread (nil, 0,@threadproc, pointer (self), create_suspended, fthreadid);
Aquí usamos la función Delphirtl BeginThread mencionada anteriormente. El tercer parámetro es la función de subproceso mencionada anteriormente, es decir, la parte del código ejecutada en el hilo. El cuarto parámetro es el parámetro pasado a la función de subproceso, aquí está el objeto de subproceso creado (es decir, self). Entre otros parámetros, el quinto se usa para establecer el hilo para suspender después de la creación y no ejecutar de inmediato (el trabajo de iniciar el hilo se determina en la construcción posterior en función del indicador CreateSoSpended), y el sexto es devolver la ID de hilo.
Ahora veamos el núcleo de Tthread: la función de hilo ThreadProc. Curiosamente, el núcleo de esta clase de hilo no es un miembro del hilo, sino una función global (porque la convención de parámetros del proceso BeginThread solo puede usar funciones globales). Aquí está su código:
functionThreadProc (hilo: tthread): integer;
varilla
Freethread: booleano;
Comenzar
intentar
ifNotThread.Termined That
intentar
Thread.ExCute;
excepto
Thread.ffataTeXception: = adquireExceptionObject;
fin;
Finalmente
Freethread: = Thread.ffreeonterMinate;
Resultado: = Thread.freturnValue;
Hilo.doterminado;
Thread.ffinished: = True;
SignalSyncevent;
iffreethreadthenthread.free;
Endthread (resultado);
fin;
fin;
Aunque no hay mucho código, es la parte más importante de todo el tthread porque este código es el código que realmente se ejecuta en un hilo. La siguiente es una explicación de línea por línea del código:
Primero, juzga el indicador de la clase de hilo. está ejecutando esencialmente el código de ejecución en la clase derivada.
Por lo tanto, Execute es una función de subproceso en una clase de subprocesos.
Si se produce una excepción en Ejecutar, el objeto de excepción se obtiene a través de la adquirida Etiqueta y se almacena en el miembro de FFATALEXCeption de la clase de subprocesos.
Finalmente, se realiza algunos trabajos de finalización antes de que termine el hilo. La variable local Freethread registra la configuración de la propiedad FreeOnmined de la clase de subprocesos y luego establece el valor de retorno de subproceso en el valor del atributo de valor de retorno de la clase de subprocesos. Luego ejecute el método doterminado de la clase de subproceso.
El código del método doterminado es el siguiente:
procedimientoThread.doterminado;
Comenzar
ifassigned (FonterMinate) Thensynchronize (CallonterMinate);
fin;
Es muy simple, es llamar al método CallOnterMinate a través de Synchronize, y el código del método CallonMinate es el siguiente, que es simplemente llamar al evento OnterMinate:
proceduretThread.callonterMinate;
Comenzar
ifassigned (FonterMinate) entonces FonterMinate (Self);
fin;
Debido a que el evento OnterMinate se ejecuta en sincronización, esencialmente no es un código de subproceso, sino el código de subproceso principal (consulte el análisis de sincronización más adelante).
Después de ejecutar OnTerminate, establezca el indicador fince de la clase de hilo en verdadero.
A continuación, ejecute el proceso de SignalSynCevent, y su código es el siguiente:
ProcedesignalSyncevent;
Comenzar
SetEvent (syncevent);
fin;
También es muy simple, es establecer un evento global: Syncevent.