quarta-feira, 16 de abril de 2008

Cross-Thread com Anonymous Methods

Quando desenvolvemos aplicações clientes é comum criar threads para executar tarefas sem que o Form fique bloqueado, sem responder ao utilizador nem actualizar a sua aparência. Até aqui tudo bem, mas imaginemos que queremos por exemplo alimentar uma lista com mensagens ou alterar propriedades de alguns controlos. Se o tentarmos fazer a partir da mesma thread onde está a correr o nosso processo, obtemos erros do tipo System.InvalidOperationException com a descrição "Cross-thread operation not valid: Control 'TextBox1' accessed from a thread other than the thread it was created on."

O que se passa é que um componente que está no ecrã apenas pode ser modificado pela thread que gere o próprio ecrã. Como resolvemos isto? O método típico é com recurso a um delegate, assim:

delegate void UpdateLabelDelegate(Label label1, string text);
private void updateLabel(Label label1, string text)
{
if (label1.InvokeRequired)
{
label1.BeginInvoke(new UpdateLabelDelegate(updateLabel), label1, text);
return;
}
label1.Text = text;
}
void MyThreadMethod()
{
//more code...

updateLabel(label1, "New text");
//more code...
}

O processo até é simples: verificamos se a thread actual pode modificar o componente (label1.InvokeRequired) e se não puder ser solicitamos à thread que suporta o componente que execute o nosso código, utilizando o delegate que lhe providenciamos. Não é complicado, mas não é muito agradável ter todo este código para simplesmente alterar o texto de uma Label ou uma qualquer propriedade de um outro componente no Form.

Desde a versão 2.0 da Framework .Net que temos a possibilidade de criar Anonymous Methods. Com este recurso o código pode ficar muito mais simples:

void MyThreadMethod()
{
//more code...
label1.BeginInvoke((MethodInvoker)delegate() { label1.Text = "New text"; });
//more code...
}

Ou seja, podemos actualizar os componentes directamente da thread onde estamos a trabalhar, com uma única linha de código. Bem mais interessante, não?