Share Button

Quando se trabalha com processos paralelos o isolamento de memória com acesso comum a processos diferentes é fundamental.

Uma opção é implementar utilizando  TThreadList que mantem o controle da lista bloqueando ou liberando quando precisa acessar a área de armazenamento sem especialidades do TStringList .

De outro lado o TStringList não é threadsafe o que motiva a reescrever as funcionalidades para compartilhamento da lista entre os processos…   Ver Classes TThreadSafe….

  TThreadSafeStringList  
  //  public
    procedure Clear;
    function Count: integer;
    function IndexOf(AText: string): integer;
    function IndexOfName(AText: string): integer;
    procedure Add(AText: string; ADupl: boolean = true);
    procedure Delete(AIndex: integer);
    procedure Remove(AText: string);
    function LockList: TStringList;
    procedure UnlockList; inline;
    property Items[AIndex: integer]: string read Getitems write Setitems;
    property Delimiter: Char read GetDelimiter write SetDelimiter;
    property DelimitedText: string read GetDelimitedText write SetDelimitedText;
    function Text: string;
    property CommaText: string read GetCommaText write SetCommaText;
    property QuoteChar: Char read GetQuoteChar write SetQuoteChar;
    procedure Assing(AStrings: TStrings);
    procedure AssingTo(AStrings: TStrings);
    procedure AddTo(AStrings: TStrings);
    property Values[AName: string]: String read GetValues write SetValues;
    property Names[AIndex: integer]: String read GetNames write SetNames;

Exemplo de como utilizar as lista compartilhando em Threads diferentes mantendo o controle do acesso a lista… [Codigo]

{$R *.fmx}

procedure TForm2.Button1Click(Sender: TObject);
var
  x: integer;
begin
  strList.Clear;
  // Thread 1
  tthread.CreateAnonymousThread(
    procedure
    var
      x: integer;
    begin
      for x := 0 to random(1000) do
      begin
        strList.Add('X' + intToStr(x));
        tthread.Sleep(random(10));
      end;
      strList.Add('X-FIM');

      tthread.Queue(nil,
        procedure
        begin
          strList.AssingTo(Memo1.lines);
        end);

    end).start;

  // Thread 2
  tthread.CreateAnonymousThread(
    procedure
    var
      x: integer;
    begin
      for x := 0 to random(1000) do
      begin
        strList.Add('Z' + intToStr(x));
        tthread.Sleep(random(10));
      end;
      strList.Add('Z-FIM');

      tthread.Queue(nil,
        procedure
        begin
          strList.AssingTo(Memo1.lines);
        end);

    end).start;

end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  strList := TThreadSafeStringList.create;

end;

procedure TForm2.FormDestroy(Sender: TObject);
begin
  strList.free;
end;

Share Button

Um boa dor de cabeça é resolver exceções em TThread, TTasks….

Base de Conhecimento
Como pré-requisito é preciso ter em mente (“recomenda-se”) não existir uma exceção não tratada dentro de uma TThread – então todos os processo deveriam tratar as suas exceções internamente com um Try/Exception.

   Try
      código com erro...
   Except
     on e:exception do
        fazer algo...
   end;

 

No framework parte do “post” LogEvents é possível prever o tratamento de exception acrescentando o método RUN…

procedure TLogListItems.Run(proc: TProc);
begin
  TThread.CreateAnonymousThread(
    procedure
    begin
      try
        proc;
      except
        on e: exception do
          LogEvents.DoErro(nil, 0, e.message);
      end;
    end).start;
end;

Com método RUN recebendo um ANONIMOUS Procedure – permite que a aplicação faça assim:

  // usado metodo ANONIMOUS para tratar exception internamente
  LogEvents.Run(
  procedure begin
        // código com potencial de exceção

       // força um exception
       raise Exception.Create('Error Message');
  end);

Como mostrar o erro ao usuário

O Framework “LogEvents” possui um método de inicialização “register” que o formulário irá se inscrever para receber mensagens… e outro para retirar a inscrição “unregister“.

LogEvents.register(self, DoErro, 0); // recebe os registro de ERROS

onde:

  • self é o formulário que irá recebe mensagens…
  • DoErro é o método do furmulario…
  • e o terceiro parâmetro é um identificador que qualifica o tipo de mensagem que irá receber

O mesmo formulário pode subscrever a receber mais de um tipo de mensagem;


    LogEvents.register(self, DoErro, 0); // recebe os registro de ERROS
    LogEvents.register(self, DoSucesso, 1); // registra para receber os sucessos

Para não receber mensagens – em geral quando o formulário fecha “close” usar: LogEvents.unregister(self);

Enviando mensagem para o formulário

O método genérico “Log” permite enviar uma mensagem para o identificador “0” (usado no register):

LogEvents.Log(‘Minha mensagem a ser mostrada’);

Para enviar uma mensagem com um identificador específico:

LogEvents.DoErro(nil, 1, ‘LOG…..’);  // register = 1

Código de Exemplo: LogEvents – Mostrando Erros ao usuário

 

 

Share Button

[usa LogEvents]
Tenho uma quantidade de produtos relativamente grande que requer processamento de custos de produção envolvendo custo de matérias primas, mão-de-obra e outros custos vinculado a célula de produção.

A modelagem prevê que uma ficha de produção pode conter outras fichas formando uma lista de dependências dos processos o que gera processamento recursivo de dependências.

Como se pode imaginar, não é um processamento sequenciado tão simples e pode ser demorado em face a profundidade da arvore de dependência que um produto pode exigir.

Então repensando os processos, o desafio passou exigir processamento em paralelo das fichas de tal forma que fosse possível processar uma quantidade de produtos ao mesmo tempo e aproveitando melhor os recursos da máquina;

Neste cenário, saber qual o estágio de processamento de cada ficha e o onde se encontra o cálculo passou a ser requisito de interação com usuário;

Para executar vamos utilizar da biblioteca de processamento em paralelo do Delphi (introduzido no XE7, no exemplo usamos Berlin).

Passos:

  • Isolar as conexões de banco de dados para trata-las individualmente por Task;
  • Criar infraestrutura de comunicação entre o processamento e feedback com usuário;
  • Tratar a sincronização de informações geradas pelas várias TTasks em andamento informando a janela de progresso do usuário;
imagem_janela
Tendo em mente que o controle possa ser utilizado em outras aplicações, o uso de um procedimento ANONIMOUS me parece bastante resistente a diversidade de códigos a que poderá vir a ser utilizado.
Veja como ficou o exemplo de execução:

procedure TForm8.Button1Click(Sender: TObject);
var
  LProgr: IProgressEvents;
  i: integer;
begin
  // inicializa a janela de progresso
  LProgr := TProgressEvents.new;
  LProgr.max := 100;  // opcional: marca o número máximo itens
  LProgr.MaxThreads := SpinEdit1.Value ;  // indica o número máximo de threads em paralelo
  LProgr.CanCancel := true;    :// marca se pode cancelar a operação

  for i := 1 to 100 do
  begin   // loop de demonstração - simulando uma lista de processos
    LProgr.Text := 'Produto: ' + intToStr(i);   // texto livre

    // onde as coisas acontecem.....
    // adiciona o processo a ser executado e aponta o método anonimous as ser executado pela TTask
    LProgr.add(i, 'Produto: ' + intToStr(i),    // processo a executar
      procedure(x: integer)
      var
        n: integer;
        msg: string;
      begin
        msg := 'Produto: ' + intToStr(x);    // processo em execução
        LogEvents.DoProgress(self, 0, etStarting, msg);  // notifica que o processo foi iniciado
        n := Random(10000);
        
        sleep(n);
        LogEvents.DoProgress(self, 0, etWorking, msg); // notifica que esta em execução
        // executa o código de calculo ... aqui...
        n := Random(10000);
        if LProgr.Terminated then exit;    // checa se o usuario cancelou a operação
        sleep(n);
      end);
    if LProgr.Terminated then
      break;
  end;
  LogEvents.DoProgress(self, 0, etAllFinished, '');  // sinaliza que todas os processo foram completados.
end;


Código fonte com o Exemplo e classes que implementam a janela de monitoramento do progresso de cada thread.