sábado, 17 de maio de 2008

Como iniciar uma thread com múltiplos parâmetros

Ainda hoje surgiu esta questão. Necessito de executar um pedaço de código numa Thread separada mas tenho de lhe passar um conjunto de parâmetros. A solução clássica passa por criar uma classe que expõe as propriedades necessárias e executar a thread numa instância dessa classe. Algo do género:

MyThreadClass myThreadClass1 = new MyThreadClass() { P1 = 1, P2 = "teste" };
ThreadStart threadStart1 = new ThreadStart(myThreadClass1.start);
Thread thread1 = new Thread(threadStart1);
thread1.Start();
class MyThreadClass
{
public int P1 { get; set; }
public string P2 { get; set; }
public void start()
{
//algum código...
}
}

Pode-se também criar uma struct ou classe simples onde se colocam os parâmetros necessários e utilizar o ParameterizedThreadStart, mas a questão mantêm-se: se o código a executar paralelamente é algo muito simples, não deveria haver uma solução mais simples? Claro que há. O construtor da classe Thread pode receber uma ThreadStart ou uma ParameterizedThreadStart. Ambos são delegates. Porque não construir ThreadStart que aponte para um simples delegate que por sua vez chama o método que se pretende paralelo? Como por exemplo:

ThreadStart threadStart1 = delegate { threadWith2Parameters("texto 1", "texto 2"); };
Thread thread1 = new Thread(threadStart1);
thread1.Start();
private void threadWith2Parameters(string i1, string i2)
{
//algum código...
}

Assim é possível executar qualquer método que esteja incluído na mesma classe independentemente dos seus parâmetros. Compactando mais o código fica apenas:

(new Thread((ThreadStart)delegate { threadWith2Parameters("texto 1", "texto 2"); })).Start();

Em ambientes WinForms é quase sempre preferível executar tarefas possivelmente demoradas em Thread's paralelas para evitar o congelamento da janela. Com este tipo de mecanismos evita-se que o código fique demasiado complexo. Imagine-se por exemplo um botão que executa um qualquer serviço remoto:

private void button1_Click(object sender, EventArgs e)
{
(new Thread((ThreadStart)delegate { listClients("sousa", "lisboa"); })).Start();
}
private void listClients(string name, string address)
{
this.BeginInvoke((MethodInvoker)delegate()
{
//método para bloquear controlos...
});
try
{
//algum código...
}
catch (Exception ex)
{
//tratar o erro...
}
finally
{
this.BeginInvoke((MethodInvoker)delegate()
{
//método para desbloquear controlos...
});
}
}

Existem por ai outras abordagens. Fica apenas mais uma.

Sem comentários: