Classe de threads em Delphi-(1)
Classe de threads em Delphi-(1) Raptor (trabalho original)
Palavra -chave ThreadEventCriticalSectionSynchronize
Aulas de tópicos em Delphi
Raptors [Mentalstudio]
http://mental.mentsu.com
(um)
Existe um thread Class TTHREAD em Delphi que é usado para implementar a programação multi-thread. O sincronizar está finalizado. No entanto, essa não é a programação multithread. O objetivo da minha escrita é adicionar a isso.
Um thread é essencialmente um pedaço de código em execução simultaneamente em um processo. Um processo possui pelo menos um thread, o chamado thread principal. Também pode haver vários tópicos infantis. Quando mais de um thread é usado em um processo, ele é chamado de "multi-threading".
Então, como esse chamado "um pedaço de código" é definido? Na verdade, é uma função ou processo (para Delphi).
Se você usar a API do Windows para criar threads, ela será implementada por meio de uma função da API chamada createethread, que é definida como:
Handlecreathread (
Lpsecurity_attributeslpthreadattributes,
DWordDwStacksize,
Lpthread_start_routinelpstartaddress,
Lpvoidlpparameter,
DWordDWcreationFlags,
LpdwordlpthreadId
);
Seus parâmetros são mencionados em seus nomes, a saber: atributos de threads (usados para definir atributos de segurança do thread em NT, inválidos abaixo de 9x), tamanho da pilha, endereço de partida, parâmetros e sinalizadores de criação (usados para definir encadeamentos o status no tempo de criação) , ID do thread e finalmente retorne a alça do encadeamento. O endereço inicial é a entrada da função do encadeamento e o encadeamento termina até que a função do encadeamento termine.
O processo de execução de todo o tópico é o seguinte:
Como os parâmetros de createThread são muitos e é uma API do Windows, uma função de rosqueamento geral é fornecida no Cruntimelibrary (em teoria, ela pode ser usada em qualquer sistema operacional que suporta threads):
Unsignedlong_beginthread (void (_userEntry*__ start) (void*), não assinado__stksize, void*__ arg);
Delphi também fornece uma função semelhante com a mesma função:
functionBeginthread (SecurityAttributes: Pointer; StackSize: Longword; Threadfunc: TTHReadfunc; Parâmetro: Pointer; CreationFlags: LongWord; VarThreadId: Longword): Inteiro;
As funções dessas três funções são basicamente as mesmas. A maior diferença entre as funções do encadeamento e as funções gerais é que, assim que a função de encadeamento é iniciada, esses três threads iniciam as funções retornam e o encadeamento principal continua a executar para baixo, enquanto a função do encadeamento é executada em um thread independente é preciso executar e o que quando retornar, o thread principal não se importa com isso ou sabe.
Em circunstâncias normais, após o retorno da função do thread, o thread termina. Mas existem outras maneiras:
API do Windows:
VoidexitThread (dWordDwexitCode);
Cruntimelibrary:
void_endThread (void);
Delphiruntimelibrary:
ProcedureEndThread (EXITCODE: INTERGER);
Para registrar alguns dados de encadeamento necessários (Estado/Propriedades, etc.), o SO criará um objeto interno para o thread. o thread termina.
Embora a programação multi-thread possa ser executada facilmente usando a API ou RTL (RunTimelibrary), ainda é necessário um processamento mais detalhado.
Também é muito simples usar esta classe. Esta é a função do thread, que é a parte do código executada no thread). Para detalhes específicos sobre isso, não os repetirei aqui, consulte os livros relevantes.
Em seguida, neste artigo, discutiremos como a classe TTHread encapsula threads, ou seja, realizaremos pesquisas aprofundadas sobre a implementação da classe TTHread. Porque é apenas melhor usá -lo se você realmente entender.
Abaixo está a declaração da classe TTHRead em Delphi7 (este artigo discute apenas a implementação na plataforma Windows; portanto, todo o código sobre a parte da plataforma Linux é removido):
TThread = classe
Privado
FHANDLE: THANDLE;
FthreadID: THANDLE;
Freatespended: boolean;
Fimminado: booleano;
Fsuspended: boolean;
FFREEONTERMINATE: booleano;
Ffinished: booleano;
FreturnValue: Inteiro;
FONTERMINATE: TNOTIFYEVENT;
Fsynchronize: TsynchronizerCord;
Ffatalexception: tabjas;
ProcedureCallOnMerminate;
ClassProceduSyChronize (assíncrono: psynchronizerCord);
functionGetPriority: tThreadPriority;
ProcedimentosEtPriority (Valor: TTHReadPriority);
ProcedimentosSetSpended (Valor: Boolean);
protegido
ProcedureCheckThreadError (Errcode: Integer); sobrecarga;
ProcedureCheckThreadError (sucesso: booleano); sobrecarga;
Procedduzoterminate; virtual;
ProcedureExecute; Virtual; Resumo;
Procedimentos Synchronize (Método: TTHReadMethod); sobrecarga;
PropertyReturnValue: IntegerReadFReturnValueWriteFreturnValue;
Propriedade do terminado: BooleanReadFerminated;
público
construtorCreate (CreateSuspended: boolean);
DestructorDestroy; substituir;
ProcedureafterConstruction; substituição;
Procedatorsume;
ProceduresEnspend;
ProcedureTerminate;
functionwaitfor: longword;
ClassProceduSyChronize (ATHREAD: TTHREAD; AMETHOD: TTHREADMETHOD); Sobrecarga;
ClassProcedureStaticsynchronize (ATHREAD: TTHREAD; AMETHOD: TTHREADMETHOD);
PropertyFatalexception: TobjectffatException;
PropertyFreeOnMerminate: BooleanReadfreeOnMerminEWriteFreeOnMerminate;
PropertyHandle: Thandlereadfhandle;
Propriedade prioridade: TTHReadPriorityReadGetPriorityWriteSetPriority;
Propriedadesuspender: booleanReadfsuspendedWriteSetSpended;
PropertyThreadId: THANDLEREADFTHREADID;
PropertyOnMerminate: tnotifyEventReadFonMermAminEWriteFonMerminate;
fim;
A classe TTHread é uma classe relativamente simples no Delphi RTL, com muitos membros da classe e os atributos da classe são muito simples e claros.
(continua)
Classe de threads em Delphi-(2)
Classe de threads em Delphi-(2) Raptor (trabalho original)
Palavra -chave ThreadEventCriticalSectionSynchronize
Aulas de tópicos em Delphi
Raptors [Mentalstudio]
http://mental.mentsu.com
Dois
O primeiro é o construtor:
constructortThread.create (CreateSuspended: boolean);
Começar
herdedcreate;
AddThread;
FSUSPENDEN: = CrieSuspended;
FCREATESSPENDEND: = CreateSuspended;
FHandle: = BEGNTHREAD (NIL, 0,@ThreadProc, Pointer (self), create_suspended, fthReadId);
iffhandle = 0then
RaiseethRead.CreaReSfmt (@SthReadCreateError, [SySerRormessage (getLasterRor)]);
fim;
Embora esse construtor não tenha muito código, ele pode ser considerado o membro mais importante porque o thread é criado aqui.
Depois de ligar para o TObject.create através do herdado, a primeira frase é chamar um processo: AddThread, seu código -fonte é o seguinte:
ProcedureAddThread;
Começar
Intertravamento (ThreadCount);
fim;
Há também um RemoveThread correspondente:
ProcedatorialEMoveThread;
Começar
Intertravamento de bloqueio (ThreadCount);
fim;
Sua função é muito simples, que é contar o número de encadeamentos no processo adicionando ou diminuindo uma variável global. No entanto, o processo INC/DEC comumente usado aqui não é o processo de Inc/DEC comumente usado, mas o processo de intertravamento/intertravamento é usado. Mas há uma maior diferença entre eles, isto é, interlockedincrement/interlockedDecrement, é segura por threads. Ou seja, eles podem garantir que os resultados da execução estejam corretos em multithreading, mas o INC/DEC não. Ou em termos de teoria do sistema operacional, este é um par de operações "primitivas".
Veja a adição como exemplo para ilustrar a diferença nos detalhes da implementação dos dois:
De um modo geral, há três etapas depois de decompor a operação de adicionar uma aos dados da memória:
1. Leia os dados da memória
2. Adicione um dados
3. Salve na memória
Agora, suponha que uma operação de adicionar um usando o INC em um aplicativo de dois thread possa ocorrer:
1. Tópico A lê dados da memória (assumindo que é 3)
2. Tópico B lê dados da memória (também 3)
3. Tópico A adiciona um aos dados (agora é 4)
4. Tópico B adiciona um aos dados (agora também é 4)
5. Thread A armazena dados na memória (os dados na memória agora são 4)
6. A Thread B também armazena dados na memória (os dados na memória ainda estão 4 agora, mas ambos os threads adicionam um a ele, o que deve ser 5, então um resultado de erro aparece aqui)
Não há problema com o processo de intertravamento, porque o chamado "primitivo" é uma operação ininterrupta, ou seja, o sistema operacional pode garantir que nenhuma troca de encadeamento seja executada antes que um "primitivo" seja executado. Portanto, no exemplo acima, somente após o thread a terminado de armazenar dados na memória, o thread B pode começar a buscar o número e adicionar uma operação, o que garante que, mesmo em situações multi-threading, o resultado definitivamente estará correto.
O exemplo anterior também ilustra uma situação de "conflito de threads", e é por isso que os threads precisam de "sincronizar".
Falando em sincronização, há uma distração: Li Ming, professor da Universidade de Waterloo, no Canadá, levou as objeções à tradução da palavra sincroniza como "sincronização" em "Sincronização de threads". muito razoável. Em chinês, a "sincronização" significa "ocorre ao mesmo tempo", enquanto a "sincronização de roscas" se destina a evitar isso "ocorrem ao mesmo tempo". Em inglês, o sincronizar tem dois significados: um é a sincronização no sentido tradicional (ToCocuratthesametime), e o outro é a "coordenação". A palavra sincroniza em "Sincronização de Thread" deve se referir ao último significado, ou seja, "para garantir que vários threads mantenham a coordenação e evitem erros ao acessar os mesmos dados". No entanto, ainda existem muitas palavras como essa que não são traduzidas com precisão na indústria de TI. deve ser esclarecido.
Eu vou longe e voltarei ao construtor do TTHREAD.
FHandle: = BEGNTHREAD (NIL, 0,@ThreadProc, Pointer (self), create_suspended, fthReadId);
Aqui, usamos a função Delphirtl Beginthread mencionada acima. O terceiro parâmetro é a função de encadeamento mencionada acima, ou seja, a parte do código executada no thread. O quarto parâmetro é o parâmetro passado para a função Thread, aqui está o objeto de encadeamento criado (ou seja, eu). Entre outros parâmetros, o quinto é usado para definir o thread para suspender após a criação e não executar imediatamente (o trabalho de iniciar o thread é determinado na pós -construção com base no sinalizador CreateSpended) e o sexto é retornar o ID do encadeamento.
Agora, vamos olhar para o núcleo do TTHREAD: a função Thread ThreadProc. É interessante Aqui está o seu código:
FunctionThreadProc (Thread: tThread): Inteiro;
var
Freethread: booleano;
Começar
tentar
ifnotThread.FromatEnThen
tentar
Thread.execute;
exceto
Thread.ffatalexception: = adquireexceptionObject;
fim;
Finalmente
Freethread: = Thread.ffreeOnMerminate;
Resultado: = Thread.freturnValue;
Thread.Doterminate;
Thread.ffinished: = true;
Signalsyncevent;
iffreethReadThenthread.free;
EndThread (resultado);
fim;
fim;
Embora não haja muito código, é a parte mais importante de todo o TTHread porque esse código é realmente executado em um thread. A seguir, é apresentada uma explicação de linha a linha do código:
Primeiro, julgue o sinalizador encerrado da classe Thread. está executando essencialmente o código de execução na classe derivada.
Portanto, executar é uma função de encadeamento em uma classe de encadeamento.
Se ocorrer uma exceção no Execute, o objeto de exceção será obtido através do ACTIREEXCCOMPECTOBJECT e armazenado no membro Ffatalexception da classe Thread.
Finalmente, há algum trabalho final feito antes que o tópico termine. A variável local Freethread registra a configuração da propriedade livre de terminação da classe Thread e, em seguida, define o valor de retorno do thread para o valor do atributo de valor de retorno da classe Thread. Em seguida, execute o método Doterminate da classe Thread.
O código do método Doterminate é o seguinte:
ProcedureTThread.Doterminate;
Começar
IfAssigned (FoNterminate) Thensincronize (callonMermine);
fim;
É muito simples, é chamar o método CallOnMerminate por meio de sincronizar, e o código do método CallOnMermine é o seguinte, que é simplesmente chamar o evento OnTerminate:
ProcedureTThread.CallOnMerminate;
Começar
ifAssigned (foNterminate) ThenFonMerminate (self);
fim;
Como o evento OnTerminate é executado em sincronizar, ele não é essencialmente o código do encadeamento, mas o código do encadeamento principal (consulte a análise de sincronizar posteriormente).
Depois de executar o OnTerminate, defina o sinalizador Ffinished da classe Thread como TRUE.
Em seguida, execute o processo Signalsyncevent, e seu código é o seguinte:
ProcedureSignalsyncevent;
Começar
SetEvent (Syncevent);
fim;
Também é muito simples, é configurar um evento global: Syncevent.