Back-End

21 jan, 2011

Programando threads em C#

Publicidade

Todo programa desenvolvido em C# possui uma thread. Esta é conhecida como thread principal. Muitos programas geralmente precisam realizar tarefas que levam um longo tempo. Se a thread principal do aplicativo for dedicada a isto, o aplicativo pode parar de responder até que a execução esteja concluída.

Para permitir que um aplicativo execute uma tarefa e continue a responder, ou para realizar várias tarefas ao mesmo tempo, podemos programar conceitos de multitarefa (multithreading).

Além de realizar processamento em segundo plano, é uma ótima maneira de melhorar o desempenho das tarefas do processador em computadores com vários processadores ou núcleos. Se um computador tem vários processadores ou vários núcleos em um único processador, ele pode executar múltiplas tarefas simultaneamente, mesmo que elas exijam tempo de processamento.

Portanto, acrescentar multitarefa em uma aplicação pode reduzir o tempo que leva para completar uma tarefa.

Criando uma thread simples

Existem várias formas de programarmos execução em segundo plano nos softwares que desenvolvemos. Uma delas é criar um método dentro da classe exclusivo para execução da tarefa em segundo plano e outro responsável pela chamada desta tarefa, como é demonstrado no código a seguir.

// C#
public class GerenciaThread
{
public void ChamarThread()
{
System.Threading.ThreadStart ts =
new System.Threading.ThreadStart(ExecutarThread);
System.Threading.Thread t =
new System.Threading.Thread(ts);
t.IsBackground = true;
t.Start();
}

private void ExecutarThread()
{
// Código a ser executado pela thread
}
}

Repare que no exemplo acima, o único método público dentro da classe ‘GerenciaThread’ é o ‘ChamarThread’. Coloquei desta forma para evitar que o método ‘ExecutarThread’ seja chamado em outros locais a não ser nesta mesma classe, e ,desta forma, permitir que o processo seja executado apenas através da thread em background.

Para iniciar o processamento em segundo plano, basta chamar o método ‘ChamarThread’.

// C#
GerenciaThread gt = new GerenciaThread();
gt.ChamarThread();

Passando parâmetros para a Thread – parte 01

Em algumas situações é necessário passar informações para a thread para serem usadas durante o processamento. Essa passagem de dados pode ser feita de várias maneiras. Uma delas é criar um construtor na classe que executa a thread permitindo um ou mais parâmetros. Ao criar a instância da classe, use este construtor e passe os parâmetros desejados.

Por exemplo, podemos executar logs de erros em threads em background. Mas para que o log seja armazenado em algum repositório, é necessário informar para a thread algumas informações como o erro ocorrido, onde ele ocorreu, etc. Usando o mesmo conceito do exemplo anterior, podemos colocar no construtor da classe ‘GerenciaThread’ os parâmetros necessários para gravação do log, como o código abaixo demonstra.

// C#
public class GerenciaLogErro
{
private Exception exception;

// Nome do projeto onde ocorreu o erro
private string strNomeProjeto;

// Nome da classe onde ocorreu o erro
private string strNomeClasse;

// Nome do método onde ocorreu o erro
private string strNomeMetodo;

public GerenciaLogErro(Exception _exception,
string _projeto,
string _classe,
string _metodo)
{
this.exception = _exception;
this.strNomeProjeto = _projeto;
this.strNomeClasse = _classe;
this.strNomeMetodo = _metodo;
}

public void ExecutarLogErro()
{
// Gravar Log no Repositório
// ou no Log de Eventos do Windows
}
}

public class LogErro
{
public void LogarErro(Exception exception,
string strNomeProjeto,
string strNomeClasse,
string strNomeMetodo)
{
GerenciaLogErro gle = new
GerenciaLogErro(exception,
strNomeProjeto,
strNomeClasse,
strNomeMetodo);

System.Threading.ThreadStart ts = new
System.Threading.ThreadStart(gle.ExecutarLogErro);
System.Threading.Thread t = new
System.Threading.Thread(ts);
t.IsBackground = true;
t.Start();
}
}

Repare que na classe ‘GerenciaLogErro’ foi criado um construtor que recebe 4 parâmetros, e esses parâmetros serão gravados no repositório para registro do Log.

A chamada para o método que executa o log pode ser feita conforme o código a seguir.

// C#
try
{
    //Coloque aqui o código que deverá ser executado no método
    Console.WriteLine(\"Esta é a Thread Principal\");
}
catch (Exception ex)
{
    LogErro logErro = new LogErro();
    logErro.LogarErro(ex,
        \"ThreadPassingData\",
        \"Program\", \"Main\");
}

Passando parâmetros para a Thread – parte 02

Outra forma de passar dados para a thread é criando uma classe que conterá propriedades que representarão os parâmetros que devem ser passados para a thread, como o código abaixo demonstra usando o mesmo exemplo de gerenciamento de logs.

// C#
public class DadosLog
{
    public Exception Exception { get; set; }
    public string StrNomeProjeto { get; set; }
    public string StrNomeClasse { get; set; }
    public string StrNomeMetodo { get; set; }
}

Em seguida, o método que executará o processo em segundo plano deve ter como parâmetro de entrada um object, que em seguida será convertido para o tipo DadosLog.

// C#
public void ExecutarLogErro(object _dadosLog)
{
DadosLog dadosLog = (_dadosLog as DadosLog);

// Gravar Log no Repositório ou no Log de Eventos do Windows
}

Para concluir, passamos a instância da classe DadosLog no método Start() no momento da chamada à thread. Veja:
// C#
public void LogarErro(Exception exception,
string strNomeProjeto,
string strNomeClasse,
string strNomeMetodo)
{
DadosLog dadosLog = new DadosLog()
{
Exception = exception,
StrNomeClasse = strNomeClasse,
StrNomeMetodo = strNomeMetodo,
StrNomeProjeto = strNomeProjeto
};

GerenciaLogErro gle = new GerenciaLogErro();
Thread t = new Thread(gle.ExecutarLogErro);
t.IsBackground = true;
t.Start(dadosLog);
}

Conclusão

Usar Threads é sempre vantajoso no ponto de vista de que podemos aproveitar o poder de processamento dos processadores que possuem mais de um núcleo, pois podemos programar para que cada thread tenha o seu processamento realizado por núcleos diferentes. Mas é sempre importante avaliar corretamente a utilização de threads, pois a concorrência poderá causar deadlocks e funcionamento incorreto de um procedimento.

Código fonte

Os códigos fontes podem ser baixados nos seguintes links:

Referências