Clique para saber mais...
  Home     Download     Produtos / Cursos     Revista     Vídeo Aulas     Fórum     Contato   Clique aqui para logar | 15 de Novembro de 2025
  Login

Codinome
Senha
Salvar informações

 Esqueci minha senha
 Novo Cadastro

  Usuários
68 Usuários Online

  Revista ActiveDelphi
 Assine Já!
 Edições
 Sobre a Revista

  Conteúdo
 Apostilas
 Artigos
 Componentes
 Dicas
 News
 Programas / Exemplos
 Vídeo Aulas

  Serviços
 Active News
 Fórum
 Produtos / Cursos

  Outros
 Colunistas
 Contato
 Top 10

  Publicidade

  [Artigos]  [Intermediário] - Tornando recursos Thread-Safe no Delphi
Publicado por rboaro : Terça, Agosto 13, 2013 - 07:49 GMT-3 (702 leituras)
Comentários 3 Comentários   Enviar esta notícia a um amigo Enviar para um amigo   Versão para Impressão Versão para impressão
Diego Garcia Já falamos aqui da necessidade de utilizar threads para criar processos paralelos, fazendo com que esses processos sejam mais ágeis e torne possível para o usuário continuar suas tarefas mesmo que, internamente o aplicativo esteja executando diversas tarefas pesadas. Porém, um problema do ambiente multi-thread é a concorrência. Imagine que duas ou mais threads façam uso do mesmo recurso, caso o acesso a esse recurso seja simultâneo e este não esteja preparado para esse cenário, é possível que sua aplicação entre em deadlock. Para evitar esses problemas existe a sessão crítica, disponível no Delphi através da classe TCriticalSection. A ideia é muito simples, um recurso compartilhado entre threads é um recurso crítico, sendo assim, antes de fazer uso desse recurso, é necessário indicar que o processamento estará entrando em uma sessão crítica e consecutivamente, indicar que o processamento saiu da sessão crítica. Por exemplo:

function TGerenciadorRecurso.AdicionarObjeto(AObjeto: TObject):integer;
begin
Self.FCritical.Enter();
try
Result := Self.FRecurso.Add(AObjeto);
finally
Self.FCritical.Release();
end;
end;

Com o TCriticalSection, somente uma thread por vez poderá entrar na sessão crítica, ou seja, quando uma thread acionar o comando:

Self.FCritical.Enter();

Qualquer outra thread que precisar entrar em uma sessão crítica do objeto FCritical (do tipo TCriticalSection) terá que esperar o comando:

Self.FCritical.Release();

Ou seja, com isso criamos um enfileiramento para consumo do recurso. Funciona como um semáforo, a partir do momento em que entramos em uma sessão crítica, o sinal está vermelho para qualquer outro processamento que tenha a necessidade de entrar nessa sessão. É importante salientar que o objeto de sessão crítica, deve estar no escopo do objeto concorrente, para que se faça sentindo o uso desse recurso, sendo assim, ao se projetar uma classe que contenha algum recurso crítico que deverá ser acessado por múltiplos processamentos paralelos, essa classe deverá ter um objeto de controle sessão crítica (TCriticalSection) para viabilizar de forma segura esse cenário.

type
TGerenciadorRecurso = class
private
FRecurso : TObjectList;
FCritical : TCriticalSection;
public
function AdicionarObjeto(AObjeto : TObject):integer;
constructor Create;
destructor Free;
end;

implementation

constructor TGerenciadorRecurso.Create;
begin
Self.FRecurso := TObjectList.Create;
Self.FRecurso.OwnsObjects := true;
Self.FCritical := TCriticalSection.Create;
end;

destructor TGerenciadorRecurso.Free;
begin
Self.FRecurso.Free;
Self.FCritical.Free;
end;

Isso porque o próprio objeto deve ter controle de seus recursos. Porém, existe um problema com o uso do TCriticalSection, esse recurso pode deixar o processo como um todo mais lento de forma desnecessária. Imagine que, seguindo nosso exemplo, eu vou escrever em um recurso crítico, mas também vou fazer leitura de seu conteúdo. O processo de leitura não necessariamente irá alterar o estado do recurso, diferente do processo de escrita. Sendo assim o processo de escrita é crítico e o de leitura não? Errado, imagine que enquanto você está fazendo a leitura do recurso, outro processo, realiza uma edição exatamente no ponto que o primeiro processo está realizando a leitura, o que poderia acontecer ao seus processos? Sendo assim, os dois processos são críticos, mas, chegamos a conclusão que podemos ter N leitoressimultâneos, porém somente um escritor, sendo que, se tiver pelo menos um leitor, não podemos realizar o processo de escrita e visse versa. Esse é o problema do TCriticalSection, com ele somente é possível um leitor por vez. Para resolver esse problema, existe a classeTMultiReadExclusiveWriteSynchronizer.
O nome já é bem claro, Multi Read Exclusive Write (tradução livre: múltipla leitura, escrita exclusiva). Então, adaptando nosso exemplo para esse cenário e incluindo um método para leitura do recurso crítico, ficamos com o seguinte código de interface:

interface

uses
System.Contnrs,
System.Classes,
System.SysUtils;

type
TGerenciadorRecurso = class
private
FRecurso : TObjectList;
FMultiReadExclusiveWrite : TMultiReadExclusiveWriteSynchronizer;
public
function AdicionarObjeto(AObjeto : TObject):integer;
function PegarObjeto(const AIdx : Integer):TObject;
constructor Create;
destructor Free;
end;

E o seguinte código de implementação

implementation

function TGerenciadorRecurso.AdicionarObjeto(AObjeto: TObject):integer;
begin
Self.FMultiReadExclusiveWrite.BeginWrite();
try
Result := Self.FRecurso.Add(AObjeto);
finally
Self.FMultiReadExclusiveWrite.EndWrite();
end;
end;

constructor TGerenciadorRecurso.Create;
begin
Self.FRecurso := TObjectList.Create;
Self.FRecurso.OwnsObjects := true;
Self.FMultiReadExclusiveWrite := TMultiReadExclusiveWriteSynchronizer.Create;
end;

destructor TGerenciadorRecurso.Free;
begin
Self.FRecurso.Free;
Self.FMultiReadExclusiveWrite.Free;
end;

function TGerenciadorRecurso.PegarObjeto(const AIdx: Integer): TObject;
begin
Self.FMultiReadExclusiveWrite.BeginRead();
try
Result := Self.FRecurso.Items[AIdx];
finally
Self.FMultiReadExclusiveWrite.EndRead();
end;
end;

Note que o método AdicionarObjeto inicia e finaliza uma sessão crítica de escrita, enquanto que o método PegarObjeto inicia e finaliza uma sessão crítica de leitura. Sendo assim, N threads poderão executar o método PegarObjeto de forma simultânea, porém, somente uma poderá executar o método AdicionarObjeto por vez. Inclusive, se algum processo estiver no bloco de leitura do método PegarObjeto, nenhuma thread poderá entrar na sessão de escrita do método AdicionarObjeto. Controlar sessões críticas é fundamental no desenvolvimento de aplicações multi-thread. Caso não sejam implementados controles para os recursos concorrentes, são grandes as chances de sua aplicação entrar em deadlock e você perder muitos dias para descobrir a causa dessa falha.


Diego Garcia
http://drgarcia1986.wordpress.com
http://twitter.com/drgarcia1986


Comentários Comentários
   Ordem:  
Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos.


por: jffonseca (joao@treinacon.com.br) : Ago 15, 2013 - 10:55
(Informações sobre o membro | Enviar uma mensagem) http://http://
Cara, sensacional!!!
Agora, como posso implementar isso em um ambiente multicamadas com DATASNAP, para threads acessando o banco de dados?
Estou tendo problemas utilizando uma única conexão TSQLConnection na aplicação Servidora, que é acessada para consultas normais dos formulários no lado Cliente e que quando uma thread de consulta de agenda é disparada, ocorre um erro: Error reading data from connection. O erro já é até conhecido e relatado pelo Eric Sasse, que segundo ele ocorre por ter um único TSQLConnection.
Gostaria de uma ajuda para solucionar o problema.

Um grande abraço.


por: marlonnardi (marlon.nardi@vanguardadobrasil.com.br) : Out 16, 2013 - 11:32
(Informações sobre o membro | Enviar uma mensagem)
Excelente Conteúdo!

Estava com este tipo de problema, utilizando recursos não compartilhados.

Att, Marlon Nardi
  Edição 112

Revista ActiveDelphi

  50 Programas Fontes


  Produtos

Conheça Nossos Produtos

Copyright© 2001-2016 – Active Delphi – Todos os direitos reservados