Todo
desenvolvedor já se deparou em uma situação que precisou fazer o uso de
“Threads”. Antes de partirmos para a parte prática gostaria de discorrer um
pouco delas.
Explicando um pouquinho
As denominadas
“Threads” significam “linhas de execuções”, seria uma forma de um processo
dividir a si mesmo em duas ou mais tarefas que podem ser executadas
concorrentemente. (Fonte: https://pt.wikipedia.org/)
Um bom
exemplo disto seria o próprio Sistema Operacional, o mesmo trabalha com as
denominadas Multi-threads, onde se divide as linhas de execução aumentando sua
eficiência. Poderemos imaginar uma “Thread” como um trecho de código que é
executado em paralelo ao seu código, sendo que podem existir diversas threads
executando em um dado momento. A partir do momento que você cria uma thread, o
Sistema Operacional fica sabendo que, além de tudo que já está fazendo, há mais
código que precisa ser executado. Se existem mais threads do que
processadores/núcleos (que é o caso mais visto nos computadores pessoais e
dispositivos móveis), então o Sistema Operacional começa a "agendar um
momento" para que cada thread execute em um determinado núcleo/processador
(processo conhecido como escalonamento, ou agendamento).
Como Funciona no C#?
É importante
lembrar que todo programa desenvolvido em C# possui uma thread, denominada “Thread
Principal”. Quando montamos rotinas que demandam um tempo maior e se a thread
principal for dedicada a isto, o programa pode parar de responder até que sua
execução seja concluída. (Por exemplo, uma rotina que utiliza um laço para ler
1.000.000 de registros e a partir dele alimentar uma Barra de Progresso). Neste
caso para permitir que este mesmo programa execute esta tarefa e continue a
responder normalmente deveremos implementar o uso de “Threads”. Realizando o
processamento em segundo plano melhoramos o desempenho das tarefas do
processador consequentemente reduzindo o tempo que leva para completar esta
tarefa.
Conhecendo a Classe “Thread”
Neste tópico
irei explicar algumas das principais funcionalidades da classe Thread.
Importante lembrar que todas estas informações foram baseadas na versão 4.5 e
4.5 do .Net Framework. A classe Thread respeita a hierarquia
abaixo:
Possuindo outras classes bases para sua implementação.
Sintaxe
A sintaxe poderá ser conferida no código abaixo:
ComVisibleAttribute(true)]
ClassInterfaceAttribute(ClassInterfaceType.None)]
public sealed
class Thread : CriticalFinalizerObject, _Thread
Principais comandos (Construtores,
Propriedades e Métodos)
Antes de
partirmos para a parte prática, achei de extrema importância apontar os
principais comandos quando fazemos o uso de “Threads”. Começando pelo
Construtor seguindo pelas suas propriedades e finalizando com seus Métodos.
Conhecendo os Construtores
1) Thread(ParameterizedThreadStart)
Teremos como inicialização uma nova instância da classe Thread, especificando um delegate (o mesmo permite que um objeto seja passado para o segmento quando este for iniciado.)
Teremos como inicialização uma nova instância da classe Thread, especificando um delegate (o mesmo permite que um objeto seja passado para o segmento quando este for iniciado.)
2) Thread(ParameterizedThreadStart, Int32)
Possui as mesmas características citadas acima, adicionando como segundo parâmetro o tamanho máximo de pilha para o segmento.
Possui as mesmas características citadas acima, adicionando como segundo parâmetro o tamanho máximo de pilha para o segmento.
Inicia uma nova instância
especificando o tamanho máximo de pilha para o segmento.
Conhecendo as Propriedades
1) CurrentContext
Esta propriedade obtém o contexto atual em que o segmento está sendo executado.
Esta propriedade obtém o contexto atual em que o segmento está sendo executado.
2) CurrentCulture
Tem como objetivo obter ou definir a contura para o segmento atual.
Tem como objetivo obter ou definir a contura para o segmento atual.
3) CurrentPrincipal
Obtém ou define o principal atual da thread (para segurança baseada em função).
Obtém ou define o principal atual da thread (para segurança baseada em função).
4) CurrentThread
Esta propriedade nos informa o thread em execução no momento.
Esta propriedade nos informa o thread em execução no momento.
5) ExecutionContext
Retorna um objeto que contém todas informações sobre os diferentes contextos de segmentos que estão sendo executados no momento.
Retorna um objeto que contém todas informações sobre os diferentes contextos de segmentos que estão sendo executados no momento.
6) IsAlive
Valor do status de execução atual.
Valor do status de execução atual.
7) IsBackground
Propriedade que indica se o segmento é um segmento de plano de fundo.
Propriedade que indica se o segmento é um segmento de plano de fundo.
8) IsThreadPoolThread
Obtém um valor indicando se um segmento pertence ao thread pool gerenciado.
Obtém um valor indicando se um segmento pertence ao thread pool gerenciado.
9) ManagedThreadId
Obtém um identificador exclusivo para o segmento gerenciado atual.
Obtém um identificador exclusivo para o segmento gerenciado atual.
10) Name
Definição ou obtenção de um nome do segmento.
Definição ou obtenção de um nome do segmento.
11) Priority
Obtém ou define um valor que indica a prioridade de programação de um segmento.
Obtém ou define um valor que indica a prioridade de programação de um segmento.
12) ThreadState
Obtém
um valor que contém os estados de segmento atual.
Conhecendo os Métodos
1) Abort()
Método para encerrar a Thread.
Método para encerrar a Thread.
2) AllocateDataSlot
Este método atribui um slot sem nome de dados em todos os segmentos. Para melhor desempenho, campos de uso marcados com o atributo de ThreadStaticAttribute em vez disso.
Este método atribui um slot sem nome de dados em todos os segmentos. Para melhor desempenho, campos de uso marcados com o atributo de ThreadStaticAttribute em vez disso.
3) BeginThreadAffinity
tem como objetivo notificar um host que o código gerenciado está prestes a executar instruções que dependem da identidade do segmento físico atual do Sistema Operacional.
tem como objetivo notificar um host que o código gerenciado está prestes a executar instruções que dependem da identidade do segmento físico atual do Sistema Operacional.
4) EndThreadAffinity
Tem a finalidade de notificar um host que o código gerenciado terminou de executar instruções que dependem da identidade do segmento físico atual do sistema operacional.
Tem a finalidade de notificar um host que o código gerenciado terminou de executar instruções que dependem da identidade do segmento físico atual do sistema operacional.
5) GetData
Este método recupera o valor do slot especificado no segmento atual, dentro do domínio atual da thread atual. Para melhor desempenho, campos de uso marcados com o atributo de “ThreadStaticAttribute” em vez disso.
Este método recupera o valor do slot especificado no segmento atual, dentro do domínio atual da thread atual. Para melhor desempenho, campos de uso marcados com o atributo de “ThreadStaticAttribute” em vez disso.
6) Join()
Bloquear o segmento de chamada até que um segmento termina, para continuar a executar com padrão e o bombeamento de “SendMessage”.
Bloquear o segmento de chamada até que um segmento termina, para continuar a executar com padrão e o bombeamento de “SendMessage”.
7) ResetAbort()
Abortar o aplicativo para o segmento atual.
Abortar o aplicativo para o segmento atual.
8) SetData
Definir os dados no slot especificado no thread em execução no momento, para o domínio atual do segmento. Para melhor desempenho, use os campos marcados com o atributo de “ThreadStaticAttribute” em vez disso.
Definir os dados no slot especificado no thread em execução no momento, para o domínio atual do segmento. Para melhor desempenho, use os campos marcados com o atributo de “ThreadStaticAttribute” em vez disso.
9) Sleep(int32)
Suspende o segmento atual por um período especificado.
Suspende o segmento atual por um período especificado.
10) Start()
Faz com que o sistema operacional modifique o estado da instância atual a “ThreadState.Running”.
Faz com que o sistema operacional modifique o estado da instância atual a “ThreadState.Running”.
1) Criando um exemplo prático via
console
No primeiro
momento iremos demonstrar como devemos proceder para criar uma Thread via
console. Para isto clique “File/New/Project...” escolhendo “Console
Application”. Ver Imagem 01.
Figura 01:
Thread via console.
Este projeto
irá possuir apenas uma classe principal chamada “ThreadExemploConsole”, a que
será executada quando compilarmos e rodarmos o programa. No próprio exemplo
abaixo irei descrevendo todos os passos. Primeiramente deveremos importar
algumas bibliotecas para esta tarefa. Ver Listagem 01.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplicationThreads
{
public class ThreadExemploConsole
{
public static void ThreadProc()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("ThreadProc:
{0}", i);
Thread.Sleep(10);
}
}
Dentro da classe pública e estática iremos criar um método estático chamado
“ThreadProc()”, produziremos a Thread secundária a partir dele. Faremos um Loop
e escreveremos na tela a cada iteração realizada.
public static void Main()
{
Console.WriteLine("Thread
principal: Iniciando a segunda Thread.");
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
Thread.Sleep(20);
for (int i = 0; i < 4; i++)
{
Console.WriteLine("Thread
Principal: Executando...");
Thread.Sleep(0);
}
Console.WriteLine("Thread
principal : Método Join() esperando até a segunda Thread terminar.");
t.Join();
Console.WriteLine("Thread
principal : Segunda Thread finalizou. Pressione
Enter para finalizar programa.");
Console.ReadLine();
}
A
partir do construtor da classe Thread, passaremos como parâmetro a thread
secundária seguido do método “ThreadStart” (para iniciá-la). Lembrando que este
parâmetro é um “delegate”, necessário para utilizar o método “ThreadProc”. Logo após usaremos
os métodos: Start() e Sleep(), respectivamente para iniciar a tread principal
fazendo com que a thread secundária aguarde o término da mesma. Teremos uma
noção clara do funcionamento analisando o resultado obtido através da Imagem
02.
}
}
Listagem 01.
Listagem 01.
Figura 02: Resultado - Saída
de Dados.
2) Criando um exemplo prático via
Windows Forms
A ideia deste exemplo seria de demonstrar de uma forma simples o
uso de “Threads” em tarefas que podemos nos deparar no dia-a-dia. Quem nunca
precisou criar uma rotina para leitura de dados de uma tabela por exemplo?
Geralmente quando precisamos realizar iterações com uma grande quantidade de
elementos é imprescindível possuir uma interface amigável para o usuário, na
maioria das vezes podemos implementar o uso de uma barra de progresso junto com
as “Threads”, permitindo executar mais de um processo por vez, como foi
explicado no exemplo anterior. Com aplicações Windows Forms precisaremos
implementar o uso de Delegates, para assim podermos executar as Threads
Secundárias. Para isto adicione no formulário um “Button” e um “ProgressBar”.
Ver Figura 03.
Figura 03: Trabalhando com Threads em Windows Forms.
Abaixo a
codificação necessária, Ver Listagem 02
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Threading;
using System.Reflection;
Primeiramente importaremos algumas bibliotecas necessárias.
namespace WindowsFormsApplicationThreads
{
public partial class FrmThreads : Form
{
public FrmThreads()
{
InitializeComponent();
}
private void
btnIniciar_Click(object
sender, EventArgs e)
{
Thread
thread = new Thread(new ThreadStart(ExecutarLoop));
thread.IsBackground
= true;
thread.Start();
}
No botão “Iniciar” é onde faremos a chamada da “Thread”.
Primeiramente passaremos como parâmetro o método “ExecutarLoop” invocando o
método “ThreadStart”. A propriedade “IsBackground” especifica a thread como
plano de fundo. O método “Start()” irá inicializá-la.
.
public void ExecutarLoop()
{
int total = 100000;
int incrementaValor = 0;
SetControlPropertyValue(progressBar1,
"value", 1);
SetControlPropertyValue(progressBar1,
"minimum", 1);
SetControlPropertyValue(progressBar1,
"maximum", total);
/* Implementando Threads
*/
while
(incrementaValor < total)
{
incrementaValor =
progressBar1.Value + 1;
SetControlPropertyValue(progressBar1,
"value", incrementaValor);
}
}
Este método fará um laço para preencher uma barra de
progresso. Não podemos esquecer que trabalhar com threads junto com windows
forms deveremos fazer o uso de um “delegate” (Para maiores informações sobre
este asssunto recomendo a leitura do artigo do mês de Maio de 2015 chamado “Linguagem
C# - Delegates em Windows Forms”)
A solução abaixo foi encontrada através do seguinte site: http://shabdar.org/c-sharp.html
e comentada também em http://www.macoratti.net
delegate void SetControlValueCallback(Control oControl, string propName, object propValue);
Iremos declarar um delegate responsável por atualizar
valores da Thread Secundária.
private void
SetControlPropertyValue(Control oControl, string propName, object propValue)
{
if (oControl.InvokeRequired)
{
SetControlValueCallback d = new SetControlValueCallback(SetControlPropertyValue);
oControl.Invoke(d, new object[] { oControl,
propName, propValue });
}
else
{
Type t = oControl.GetType();
PropertyInfo[] props = t.GetProperties();
foreach (PropertyInfo p in props)
{
if
(p.Name.ToUpper() == propName.ToUpper())
{
p.SetValue(oControl, propValue, null);
}
}
}
}
}
}
Listagem 02.
Listagem 02.
Este método “SetControlPropertyValue” terá como parâmetro
de entrada o tipo de controle, a propriedade e o valor.
Podemos
conferir o resultado na Figura 04.
Figura 04: Exemplo
em Execução.
Conclusões
O uso de
“Threads” se torna uma prática muito utilizada no desenvolvimento de programas.
Determinadas tarefas que encontramos o uso deste mecanismo se torna
indispensável para aumentar a qualidade do código e do resultado final para o
usuário.
Com este artigo
procurei demonstrar os principais construtores, métodos e propriedades da
classe “Thread” junto com exemplos simples que os ajudarão a adaptar para
praticamente todas as necessidades.
Um forte
abraço e até o mês que vem!
Referências
https://msdn.microsoft.com/pt-br/library/system.threading.thread%28v=vs.110%29.aspx
Nenhum comentário:
Postar um comentário