Post: [Parelelismo em Delphi] Entendendo a biblioteca de Paralelismo

Alessandro Medeiros

Fala ai Radizeiros e Radizeiras, tudo bem com vocês?

Mais uma nova série aqui no blog que damos início.

E não podia faltar conteúdos extraordinários.

Damos início hoje falando sobre PPL.

Mas Thulio que negócio é esse de PPL?

O PPL é a biblioteca de programação paralela do Delphi.

Nessa nossa série iremos pegar ponto a ponto as features que tem dentro da biblioteca de programação paralela, e iremos esmiuçar.

Hoje iremos ter uma introdução onde você irá compreender o TTask, o que é realmente criar uma tarefa para rodar em segundo plano.

Chega de bate papo e vamos ao que interessa né?

A biblioteca de paralelismo do Delphi, resumindo, é a execução de tarefas simultâneas sem que elas ocupem a Thread principal, ou seja, o processo principal da aplicação.

Todas as vezes que você abre um projeto no Delphi, vamos dizer que ele cria uma fila de execução de tarefas.

Todo o projeto tem essa fila.

Você cria um form no Delphi, e ao executar esse projeto, é nesse momento que você entra em uma fila.

E agora se eu quiser executar uma ação, seja uma procedure ou uma function.

Vou criar uma procedure.

...
private
    procedure Teste;
...
procedure TForm1.Teste;
begin
    label1.caption := 'teste';
end;

Vamos uma ação num botão que irá chamar essa procedure Teste.

procedure TForm1.Button4Click(sender: TObject);
begin
    Teste;
end;

Todas as vezes que executamos uma procedure, uma function, seja o que for, ele cria uma fila.

Ele vai enfileirando e executando.

Essa fila é como se fosse a Thread, a tarefa principal, vamos dizer assim, a Thread principal do projeto, está rodando em uma Thread.

O fato de você visualizar uma execução do projeto, e poder mexer nele, quando passa o mouse nos botões e muda a cor.

Tudo isso está dentro de um único processo.

Então eu quero alterar o Label que tem no meu formulário.

Observe que ao clicar no botão esse nosso Label altera o nome, isso ocorre porque como ele não está executando nada nesse momento, ao clicar é executada a ação determinada no botão.

Isso tudo acontece por está rodando na Thread principal.

Mas se eu tiver que executar tarefas simultâneas?

Ao mesmo tempo que executo uma ação, preciso também fazer outra.

O que acontece?

Você precisa abrir uma outra fila que irá processar tarefas ao mesmo tempo.

E você pode abrir diversas filas, ou seja, diversas threads, para serem executadas simultaneamente.

Porém é preciso ter uma atenção, porque nem tudo que é feito no Delphi, principalmente quando se fala de banco de dados, está preparado para ser multi-Thread, ou seja, trabalhar de forma simultânea, com várias tarefas ao mesmo tempo.

Mas vamos ao nosso exemplo simples.

Se você não conhecia a Thread, ou conhece mas tem alguma dificuldade, esse post irá lhe ajudar.

Nesse nosso exemplo temos um Label e alguns botões.

No primeiro botão, com o nome Normal, eu tenho uma ação.

procedure TForm1.Button1Click(Sender: TObject);
begin
    sleep(10000);
    label1.caption := DateTimeToStr(Now);
end;

Observe que estou executando um Sleep de 10 segundos.

Lembra que falei antes sobre ter uma fila para a execução dos processos do software?

Quando clicarmos neste botão ele irá entrar em um processo, só que a execução dele diz que irá aguardar 10 segundos e depois troque o nome do Label pela data.

Se tentarmos executar uma outra ação simultaneamente, não será possível até que esse Sleep, ou seja, até que esse tarefa termine.

Ao clicar no botão Normal, onde temos essa implementação, e tentar clicar no outro botão que altera o caption do label para teste, não será possível.

Observe que não foi possivel executar outras ações, porque esse processo encontra-se no topo da fila aguardando 10 segundos.

Depois que esses 10 segundo terminar podemos executar qualquer outra ação, porque esse processo foi finalizado.

É aí que entra a biblioteca PPL, que é declarada no uses do projeto.

...
uses
    System.Threading;
...

Ele disponibiliza uma série de possibilidades para você trabalhar com as Threads.

Vamos ver aqui hoje em nosso post o TTask.

Como conseguimos executar diversos processos ao mesmo tempo?

Isso está intimamente ligado a experiência do usuário.

Muita das vezes, principalmente se for Mobile, se fizer uma ação, uma interação, alguma coisa que trave a aplicação por alguns segundos, o usuário já pensa que ocorreu algum problema, o Android já mostra a mensagem se deseja fechar ou aguarda a execução da aplicação.

Quando você começa a pensar na experiência do usuário, utilizar Thread, se torna essencial para que você ofereça para o seu usuário uma experiência mais fluida na sua aplicação.

Então como resolvemos esse probleminha do Sleep?

procedure TForm1.Button1Click(Sender: TObject);
begin
    TTask.Run(procedure
    begin
        sleep(10000);
        TThread.Synchronize(TThread.CurrentThread,
        procedure
        begin
            label1.caption:=datetimetostr(now);
        end);
    end);
end;

Observe que criamos uma Task, ou seja, uma tarefa.

Temos uma classe chamada TTask que está na biblioteca que declaramos nas uses do projeto, e ela tem uma série de opções.

Ao longo dessa nossa série de paralelismo, aqui no blog, iremos explorar uma possibilidade com essa biblioteca.

Vamos explorar para que você possa construir uma aplicação mais fluida para os seus usuários.

Nesse nosso código o TTask.Run pede um método anônimo.

Dentro deste método anônimo estamos fazendo a mesma coisas que fizemos anteriormente, onde executamos um Sleep e mexendo dentro do Label.

Mas observando o código, temos o Sleep de 10 segundos, e para alterar o caption do Label está dentro de um método diferente que é o TThread.Synchronize.

O TThread.Synchronize é muito importante, como falei, nem todos os componentes são preparados para trabalhar de forma multi-tarefas.

Então pode acontecer de tarefas diferentes está acessando aquele componente no mesmo momento, e aí, ou você vai ter algum retorno, ou alguma informação inválida, porque não esperou terminar um processo, ou até mesmo um Access Violation.

Isso é muito natural de acontecer quando você trabalha com Thread e não se atenta para esses detalhes.

O TThread.Synchronize garante que você, mesmo que esteja trabalhando com threads separadas, cria um Thread safe.

É a garantia de que de que a Thread  não vá ter um comportamento indesejado, ou seja, vai aguardar esses processos dentro da Thread, para que não tenha acessos simultâneos, e retorne valores inválidos, ou até mesmo Access Violation.

Estou garantindo, o seguinte, que essa minha Thread, apesar de ser uma Thread separada, e irá estar rodando dentro da minha Thread principal, ou seja, dentro da CurrentThread.

Isso é para que não tenhamos nenhum problema com os componentes, como no caso do Label, e outros componentes visuais que não são multi-Thread.

Desta forma é muito importante que utilize o Thread.Synchronize, principalmente quando for trabalhar com componentes visuais, ou ações de banco de dados.

Então o que estou fazendo é o seguinte.

Estou iniciando uma tarefa.

...
TTask.Run(...
...

E passo um método anônimo.

...
procedure
begin
    sleep(10000);
    TThread.Synchronize(TThread.CurrentThread,
    procedure
    begin
        Label1.Caption := DateTimeToStr(Now);
    end);
end);

Nesse método anônimo estou dizendo que ele deve ser executado, onde quero que espere 10 segundos, e o TThread.Synchronize, onde em qual Thread quero que execute uma ação, TTHread.CurrentTHread, e logo em seguida passo um outro método anônimo.

E por ultimo, dentro de outro método anonimo, ou seja, dentro da Thread atual, passo a data atual para o caption do Labe1.

Com isso ele não irá mais travar minha aplicação.

Vamos ver?

Lembra que na execução anterior, sem a Thread, tem um travamento na aplicação por uns 10 segundo.

E ao executar com TTask, eu consigo executar qualquer ação dentro do meu projeto sem que o mesmo trave, e após 10 segundos é alterado o caption do Label.

Todo processo desse projeto foi executado perfeitamente, tudo isso graças a utilização de uma Thread separada da Thread principal.

O legal que com isso posso executar qualquer tarefa dentro desse projeto sem o mesmo trave.

Muito legal não é pessoal?

Eu tenho curtido muito cada post do blog, e em poder colaborar com vocês e poder fazer com que você consiga aplicar os métodos mostrado aqui em suas aplicações.

Esse é mais um post que faz parte do CLUBE DOS PROGRAMADORES EM DELPHI, uma gama de conteúdos que com toda certeza irá agregar e muito no seu dia a dia.

Você ainda não faz parte?

Acesse o link abaixo e venha fazer parte dessa história.

E caso você tenha interesse de conhecer mais sobre PPL acessa o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI, onde você não só terá conteúdos relacionados aos generics, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.

CLIQUE AQUI E SAIBA MAIS SOBRE O CLUBE DOS PROGRAMADORES DELPHI




Faça sua busca

CATEGORIAS

POSTS RECENTES

E caso você tem interesse de conhecer mais sobre [Parelelismo em Delphi] Entendendo a biblioteca de Paralelismo, acesse o nosso portal do CLUBE DE PROGRAMADORES EM DELPHI
Você não terá só conteúdos relacionados ao [Parelelismo em Delphi] Entendendo a biblioteca de Paralelismo, mas uma quantidade enorme de conteúdos que poderá lhe ajudar muito no seu dia a dia, é uma verdadeira NETFLIX para os programadores Delphi.
Gostou?
Compartilhe:

Embarque no foguete com milhares de devs para aprender desenvolvimento, evoluir de forma contínua e se manter relevante no mercado.

Dúvidas
Cadastre-se em nossa lista

Para ter acesso em primeira mão, a tudo que acontece na Academia do Código, basta se cadastrar em nossa lista

Grupo Thulio Bittencourt | Academia do Código

#FaçaPartedaHistória

Copyright © 2024 – Todos os direitos reservados