Pense, por exemplo, que você criou uma thread para imprimir relatórios em background para seu sistema. Você disponibiliza uma lista global onde o usuário pode ir adicionando novos relatórios a serem impressos. Sua thread de impressão busca nessa mesma lista qual é o próximo relatório a imprimir.
Antes de prosseguir, lembro alguns detalhes para tornar mais claro o contexto. Quando você escreve um programa em Delphi ou outra linguagem de alto nível, o compilador converte cada linha do seu programa em diversas instruções de baixo nível que podem ser executadas pelo computador. Toda thread é, então, composta por uma sequência das instruções originadas a partir das várias linhas do código que você escreveu. O Sistema Operacional executa as threads submetendo a sequência de instruções de cada uma delas por um tempo determinado (chamado time slice). Como esse tempo é determinado pelo Sistema Operacional, não há garantias de que todo o bloco gerado a partir da linha de código Delphi será executado todo de uma vez.Na figura acima, se o tempo destinado à Thread Principal se esgotar no ponto desenhado, o estado das variáveis de programa envolvidas pode não ter sido completamente atualizado. Outra Thread que acessar uma variável nessa condição encontrará lixo.
Voltando ao exemplo da thread de impressão, é preciso garantir que o código que manipula a lista seja executado como uma unidade. Sistemas Operacionais como o Windows disponibilizam um mecanismo denominado Seção Crítica para resolver esta questão. No Delphi e C++ Builder, a classe TCriticalSection (que está na unit syncobjs) encapsula o funcionamento desse mecanismo. A ideia é simples : cria-se uma instância de seção crítica para cada recurso que se queira proteger, proteção essa que se dá envolvendo o acesso ao recurso com a chamada a 2 funções - uma para entrar na seção crítica e a outra para liberar o acesso ao recurso novamente. Ao adicionar um novo Job à lista de impressão:
try
_scPrintJobs.Enter;
_Jobs.Add (pNovoJob);
finally
_scPrintJobs.Leave;
end;
E também quando a Thread responsável pela impressão for remover um job para executá-lo:_scPrintJobs.Enter;
_Jobs.Add (pNovoJob);
finally
_scPrintJobs.Leave;
end;
try
_scPrintJobs.Enter;
lJob := _Jobs.Items[pJobIdx];
_Jobs.Delete (pJobIdx);
finally
_scPrintJobs.Leave;
end;
É conveniente utilizar tratamento de exceções (par try/finally) pois a seção crítica é um recurso bastante sensível. Se ocorrer algum problema e o método Leave nunca for executado, o seu programa fatalmente vai deixar de funcionar. É bom usar com critério o quê vai ser colocado dentro da seção crítica pois um processamento pesado pode fazer com que o restante da aplicação pare de responder._scPrintJobs.Enter;
lJob := _Jobs.Items[pJobIdx];
_Jobs.Delete (pJobIdx);
finally
_scPrintJobs.Leave;
end;
Por questão de organização, a criação da seção crítica pode ser feita na própria unit reservada ao recurso que ela deve proteger:
var _scPrintJobs : TCriticalSection;
{...}
initialization
_scPrintJobs := TCriticalSection.Create;
finalization
_scPrintJobs.Destroy;
end.
Seções Críticas devem ser utilizadas somente dentro de um mesma aplicação. Se pretende construir uma biblioteca (DLL) que poderá ser utilizada por mais de uma aplicação ao mesmo tempo, o mecanismo mais apropriado é o Mutex (acesso mutuamente exclusivo). Os Mutexes funcionam do mesmo modo que as seções críticas e, embora possam ser usados também quando o escopo é o mesmo processo, são ligeiramente mais lentos.{...}
initialization
_scPrintJobs := TCriticalSection.Create;
finalization
_scPrintJobs.Destroy;
end.
2 comentários :
Olá,
tenho um recurso de impressão que é compartilhado(metodo de impressão global) por um conjunto de threads e tbm pelo usuário ao solicitar uma impressão de relatório por exemplo.
Ao criar uma sessão critica para tratar este caso, estou tendo problema pq o semafaro parece estar sempre aberto para todas as requisições.
Existe alguma diferença entre semafaro quando pe acessado por formulário e thread?
obrigado.
Não há diferença entre o uso de semáforos na thread principal e em outras threads criadas manualmente.
Como é feita a sincronização em seu caso? Pode haver alguma falha na aplicação do recurso; liberação antes da hora, por exemplo.
[]s
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.