Trabalhando com multithread no C# 
Escrito por Daniel Nogueira • Nov 12th, 2007 • Categoria: C#, Programação
Para quem trabalhava ou ainda trabalha com Visual Basic, sabe que não pode contar com uma solução multithread, a não ser que crie um Activex por exemplo para executar esta tarefa. Mas para quem migrou para C#, se deparou com uma linguagem bem completa, graças as evoluções do framework .NET. Em C# é possível trabalhar com a tecnologia multithread. E sua utilização é muito útil para aplicações que necessitam executar processos em separado, sem perda de performance no sistema. Num novo processo você pode executar métodos lentos e demorados que pendurariam a maquina, e o resto da sua aplicação funcionara normalmente, poupando muito mais a paciência do usuário, de ficar parado olhando a aplicação pensar.
Neste artigo, vamos mostrar casos simples de uso de multithread na sua aplicação. Como execução de uma simples thread, interaçao entre threads e sincronização de threads.
O que é multithread?
Multithread é a mesma coisa que comparar o DOS com o Windows, no DOS só era possível executar uma tarefa de cada vez e não abrir várias tarefas ao mesmo tempo, como o Windows nos permite. Com multithread é criada uma ilusão de que todas as tarefas estão sendo executadas ao mesmo tempo, mas na verdade estão sendo executadas em partes, aos poucos cada processo executa um trecho do código. Assim não sobrecarrega o sistema, podendo manter várias tarefas em execução ao mesmo tempo.
Criar e executar uma Thread
1. Crie um novo projeto C# Windows Application, e o renomeie-o para MultiThreadExemplo.
2. Crie um formulário e o nomeie para clsfrmPrincipal.
3. No formulário crie um TextBox chamado textBoxEvento, altere a propriedade Multiline para True e a propriedade ScrollsBar para Vertical.
4. Insira dois ButtonBox no formulário, o primeiro com o nome butExecutarSemThread e defina a propriedade Text para “Executar sem Thread”, e o segundo para butExecutarThread e defina a propriedade Text para “Executar Thread”.
Criado a interface do exemplo, vamos para a parte de códigos. Crie o evento FormClosing no formulário principal:
private void clsfrmPrincipal_FormClosing(object sender, FormClosingEventArgs e)
{//* Fecha a aplicação
System.Environment.Exit(System.Environment.ExitCode);}
Crie o método fncMostraMensagem para mostrar uma MenssageBox para o usuário:
private void fncMostraMensagem(string strMensagem)
{//* Mostra um mensagem box
MessageBox.Show(strMensagem);return;
}
Crie o evento Click do botão butExecutarSemThread, e insira o código abaixo:
private void butExecutarSemThread_Click(object sender, EventArgs e)
{//* Limpa a textbox
textBoxEvento.Text = “”;for (long lngQuantiadeMensagens = 0; lngQuantiadeMensagens < 5; lngQuantiadeMensagens++)
{//* Executa o metodo
fncMostraMensagem(lngQuantiadeMensagens.ToString());//* Insere o indice no textbox
textBoxEvento.Text += lngQuantiadeMensagens.ToString() + Environment.NewLine;}
return;
}
Crie o evento Click do botão butExecutarThread, e insira o código abaixo:
private void butExecutarThread_Click(object sender, EventArgs e)
{System.Threading.Thread[] threadMensagem;
//* Limpa a textbox
textBoxEvento.Text = “”;for (long lngQuantiadeMensagens = 0; lngQuantiadeMensagens < 5; lngQuantiadeMensagens++)
{//* Incrementa a quantidade de itens do array
threadMensagem = new System.Threading.Thread[lngQuantiadeMensagens + 1];//* Cria uma nova ThreadStart com o metodo fncMensagem
System.Threading.ThreadStart threadstartMensagem = delegate { fncMostraMensagem(lngQuantiadeMensagens.ToString()); };//* Instancia a Thread
threadMensagem[lngQuantiadeMensagens] = new System.Threading.Thread(threadstartMensagem);//* Inicia a Thread
threadMensagem[lngQuantiadeMensagens].Start();//* Insere o indice no textbox
textBoxEvento.Text += lngQuantiadeMensagens.ToString() + Environment.NewLine;}
return;
}
Neste simples exemplo vamos executar o método fncMostraMensagem que mostra uma MessageBox com o texto passado por parâmetro. E insere o índice atual no textBoxEvento.
Ao clicar no botão butExecutarSemThread, o programa passa por um laço mostrando para o usuário uma mensagem com o índice do laço em uma MessageBox e insere o índice atual no textBoxEvento. Mas enquanto o usuário não clicar no botão OK da mensagem, a próxima não aparecera.
Ao clicar no botão butExecutarThread, executamos praticamente a mesma função, mas ao invés de chamar o método fncMostraMensagem no mesmo processo que nossa aplicação, executamos ele em separado, sendo assim, ele continua o laço, mesmo com a mensagem na tela, porque cada mensagem está num processo independente uma da outra.
Interação entre Threads
Continuando o nosso exemplo, no mesmo projeto crie mais dois ButtonBox, o primeiro com o nome butNaoInteragirThreads e defina a propriedade Text para “Não Interagir Threads”, e o segundo para butInteragirThreads e defina a propriedade Text para “Interagir Threads”.
Crie um delegate para a classe clsfrmPrincipal:
private delegate void delegateInsereTexto(string Texto);
Crie o método fncInsereTextoThread que executa um laço, e insere o índice no textBoxEvento:
private void fncInsereTextoThread(bool blnInsereDelegate)
{for (long lngCountMensagem = 0; lngCountMensagem < 10; lngCountMensagem++)
{
//* Verifica se deve inserir o delegate
if (blnInsereDelegate == true)
{//* Insere o texto na textbox
this.Invoke(new delegateInsereTexto(fncInsereTextoEvento), lngCountMensagem.ToString() + Environment.NewLine);}
else
{textBoxEvento.Text += lngCountMensagem.ToString() + Environment.NewLine;
}
}
return;
}
Insira o método fncInsereTextoEvento que insere o texto passado como parâmetro no textBoxEvento, que será usado pelo delegate que criamos anteriormente:
private void fncInsereTextoEvento(string strTexto)
{//* Insere o texto na textbox
textBoxEvento.Text += strTexto;}
Crie o envento Click do botão butNaoInteragirThreads, e insira o código abaixo:
private void butNaoInteragirThreads_Click(object sender, EventArgs e)
{System.Threading.Thread threadMensagem;
System.Threading.ThreadStart threadstartMensagem;
//* Limpa a textbox
textBoxEvento.Text = “”;//* Cria uma nova ThreadStart com o metodo fncInsereMensagemTexto não sincronizado
threadstartMensagem = delegate { fncInsereTextoThread(false); };//* Instancia a Thread
threadMensagem = new System.Threading.Thread(threadstartMensagem);//* Inicia a Thread
threadMensagem.Start();}
Crie o evento Click do botão butInteragirThreads, e insira o código abaixo:
private void butInteragirThreads_Click(object sender, EventArgs e)
{System.Threading.Thread threadMensagem;
System.Threading.ThreadStart threadstartMensagem;
//* Limpa a textbox
textBoxEvento.Text = “”;//* Cria uma nova ThreadStart com o metodo fncInsereMensagemTexto não sincronizado
threadstartMensagem = delegate { fncInsereTextoThread(true); };//* Instancia a Thread
threadMensagem = new System.Threading.Thread(threadstartMensagem);//* Inicia a Thread
threadMensagem.Start();}
Nesta segunda parte do exemplo, vamos ver como interagir threads. Criamos uma thread para chamar o método fncInsereTextoThread que executa um laço, que insere o índice no textBoxEvento. Vamos executar este método de duas maneiras, uma sem delegate e a outra com delegate.
Ao clicar no botão butNaoInteragirThreads, criamos uma thread que chama o método fncInsereTextoThread definido pelo parâmetro, para não usar delegate. Sendo assim ocorrerá um erro, como o textBoxEvento foi instanciado em outra thread ao tentar inserir o texto no textBoxEvento, a thread não consegue modifica-lo.
Para isto precisamos de um delegate, que execute este trabalho, como mostra o butInteragirThreads. Ao clicar no butInteragirThreads, é passado como parâmetro para o método fncInsereTextoThread para usar o delegate, sendo assim, quando ele alterar o textBoxEvento, o delegate executa o método fncInsereTextoEvento no processo onde se encontra o objeto.
Sincronização entre Threads
Finalizando este artigo, vou mostrar como sincronizar duas threads.
No mesmo projeto crie mais dois ButtonBox, o primeiro com o nome butNaoSincronizarThread e defina a propriedade Text para “Não Sincronizar Thread”, e o segundo para butSincronizarThread e defina a propriedade Text para “Sincronizar Thread”.
Crie uma variável boolean na classe clsfrmPrincipal:
private bool blnAguardaThread = false;
Crie o método fncInsereMensagemTextoSincronizado que executa um laço, nele é chamado o delegate para inserir no textBoxEvento (como mostrado no exemplo anterior) o índice no textBoxEvento:
private void fncInsereMensagemTextoSincronizado(long lngIndex, bool blnSincronizar)
{for (long lngCountMensagem = 0; lngCountMensagem < 10; lngCountMensagem++)
{
//* Bloqueia o bloco atual pela thread
lock (this)
{//* Verifica se deve executar a sincronizaçao entre thread
if (blnSincronizar == true)
{System.Threading.Monitor.Pulse(this);
blnAguardaThread = true;
}
//* Insere o texto na textbox
this.Invoke(new delegateInsereTexto(fncInsereTextoEvento), “Thread ” + lngIndex.ToString() + “: ” + lngCountMensagem.ToString() + Environment.NewLine);//* Verifica se deve executar a sincronizaçao entre thread
//* Verifica se deve aguardar outra thread ser executada
if (blnSincronizar == true && blnAguardaThread == true)
{blnAguardaThread = false;
//* Aguarda a outra thread terminar
System.Threading.Monitor.Wait(this);}
}
}
return;
}
Crie o evento Click do botão butNaoSincronizarThread, e insira o código abaixo:
private void butNaoSincronizarThread_Click(object sender, EventArgs e)
{System.Threading.Thread[] arrthreadMensagem;
System.Threading.ThreadStart[] arrthreadstartMensagem;
//* Limpa a textbox
textBoxEvento.Text = “”;//* Redimensiona o array
arrthreadMensagem = new System.Threading.Thread[2];
arrthreadstartMensagem = new System.Threading.ThreadStart[2];//* Cria uma nova ThreadStart com o metodo fncInsereMensagemTexto não sincronizado
arrthreadstartMensagem[0] = delegate { fncInsereMensagemTextoSincronizado(0,false); };//* Instancia a Thread
arrthreadMensagem[0] = new System.Threading.Thread(arrthreadstartMensagem[0]);//* Inicia a Thread
arrthreadMensagem[0].Start();//* Cria uma nova ThreadStart com o metodo fncInsereMensagemTexto não sincronizado
arrthreadstartMensagem[1] = delegate { fncInsereMensagemTextoSincronizado(1, false); };//* Instancia a Thread
arrthreadMensagem[1] = new System.Threading.Thread(arrthreadstartMensagem[1]);//* Inicia a Thread
arrthreadMensagem[1].Start();return;
}
Crie o evento Click do botão butSincronizarThread, e insira o código abaixo:
private void butSincronizarThread_Click(object sender, EventArgs e)
{System.Threading.Thread[] arrthreadMensagem;
System.Threading.ThreadStart[] arrthreadstartMensagem;
//* Limpa a textbox
textBoxEvento.Text = “”;//* Redimensiona o array
arrthreadMensagem = new System.Threading.Thread[2];
arrthreadstartMensagem = new System.Threading.ThreadStart[2];//* Cria uma nova ThreadStart com o metodo fncInsereMensagemTexto sicronizado
arrthreadstartMensagem[0] = delegate { fncInsereMensagemTextoSincronizado(0, true); };//* Instancia a Thread
arrthreadMensagem[0] = new System.Threading.Thread(arrthreadstartMensagem[0]);//* Cria uma nova ThreadStart com o metodo fncInsereMensagemTexto sincronizado
arrthreadstartMensagem[1] = delegate { fncInsereMensagemTextoSincronizado(1, true); };//* Instancia a Thread
arrthreadMensagem[1] = new System.Threading.Thread(arrthreadstartMensagem[1]);//* Inicia a Thread
arrthreadMensagem[0].Start();//* Inicia a Thread
arrthreadMensagem[1].Start();return;
}
E finalizando o artigo, vamos ver como criamos uma sincronização entre threads.
Ao clicar no botão butNaoSincronizarThread, criamos um array de threads, e redimensionamos ele para 2 itens. Cada item executa o método fncInsereMensagemTextoSincronizado, passando como parâmetro a index do item, e um valor boolean para não sincronizar as threads. Ao executar o método fncInsereMensagemTextoSincronizado executamos um laço onde inserimos no textBoxEvento a index da thread e o índice do laço apontado por lngCountMensagem. Como não sincronizamos as threads, os itens no textBoxEvento aparassem embaralhados não respeitando uma seqüência, de qual thread vai inserir.
Ao clicar no botão butSincronizarThread, executamos quase a mesma função do botão butNaoSincronizarThread com a diferença que, enquanto uma thread está sendo executada, esperamos ela terminar, e então executamos a outra, sendo assim, mantendo uma sincronização entre elas. Para isto usamos o método System.Threading.Monitor.Wait para entrar na fila das thread a serem executadas, e System.Threading.Monitor.Pulse para informar a fila que o objeto esta desbloqueado, e assim poder executar a próxima thread que está aguardando.
Download do Exemplo
E por ultimo, mas não menos importante, o link para download do exemplo: http://www.fdweb.com.br/downloads/csharpmultithreadexemplo.zip
Confira ofertas de: DVD, filmes, celular, notebook, livros, jogos, Wii, PS3, MP4


Daniel Nogueira gostei do artigo achei muito legal no sentido didatico, no entanto quando clico no botão “Não interagir threads”, da o seguinte erro “Cross-thread operation not valid: Control ‘textBoxEvento’ accessed from a thread other than the thread it was created on.” vc sabe o que pode ser???
Dae Osmair, seguinte este botão de “Não interagir threads” citava como exemplo o que ocorreria se tu não interagisse. Este érro está descrito no artigo.
Cara, d+ um bom exemplo vale mais do que mil palavras.
otimo artigo parabéns.
Daniel Nogueira, muito obrigado pelo vosso artigo.
O qual explicou de modo simples e natural esta tecnologia.
Neste exemplo a função que exibe mensagem é inserida na Thread.
Mas, e, supondo que a função fncMostraMensagem(string strMensagem) contenha as seguintes linhas, abaixo.
Há erro na 4 linha, muda a maneira de declarar a thread, quando há componentes?;
ESTUDO_Barra_download_continua.UserControl1 b = new ESTUDO_Barra_download_continua.UserControl1();
b.Location = new System.Drawing.Point(12, 120);
b.Size = new System.Drawing.Size(145, 150);
this.Controls.Add(b);