Ir para o conteúdo
  • Revista PROGRAMAR: Já está disponível a edição #60 da revista programar. Faz já o download aqui!

msr

Thread + ListBox

Mensagens Recomendadas

msr

Olá,

Estou aqui com um problema.

Criei uma nova WinForm e agora tenho uma thread cujo código queria que utilizasse uma LisbBox, no entanto tal não é possível. Obtenho o seguinte erro "control acessed from a thread other than the thread it was created on".

Porque é que tal não é possivel e como posso resolver a situação?

Obrigado

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
mjamado

Boas, msr.

Esse erro acontece porque - já deves ter adivinhado - não podes alterar as propriedades de um componente numa thread que não a criadora do mesmo. Há várias razões para isso, mas explicá-las está fora do âmbito desta posta.

A solução é usar um delegate para que o componente seja alterado assim que o processador tiver um tempinho disponível. Tipo isto:

delegate void SetPropriedadeComponenteCallback(Control componente, string propriedade, object valor);

private void SetPropriedadeComponente(Control componente, string propriedade, object valor)
{
    if(componente.InvokeRequired)
    {
        SetPropriedadeComponenteCallback dlgt = new SetPropriedadeComponenteCallback(SetPropriedadeComponente);
        componente.Invoke(dlgt, new object[] {componente, propriedade, valor});
    }
    else
    {
        Type tipo = componente.GetType();
        PropertyInfo[] propriedades = t.GetProperties();

        foreach (PropertyInfo p in propriedades)
            if (p.Name == propriedade)
                p.SetValue(componente, valor, null);
    }
}

/* O teu código normal, por aqui abaixo */

public void TeuThreadStarter()
{
    /* o teu código por aqui
        Quando quiseres mudar a propridade da tal listbox: */
    this.SetPropriedadeComponente(tuaListBox, "propriedadeAMudar", valorAMudar);

    // o resto do código normalmente
}

Se googlares pelo erro, o mais provável é que a primeira solução que encontres seja colocar o Control.CheckForIllegalCrossThreadCalls a falso. Não o faças. Há boas razões para essa verificação existir e deixa-a ficar, que está bem. A maneira correcta é mesmo com delegates.

Abraços!


"Para desenhar um website, não tenho que saber distinguir server-side de client-side" - um membro do fórum que se auto-intitula webdesigner. Temo pelo futuro da web.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
bruno1234

A solução do delegate é boa, no entanto é preciso ter em atenção uma coisa...

Quantas threads vais querer q actualizem a caixa? Essas threads n podem antes fazer o trabalho delas e devolver  o valor á thread principal?

Cuidado com a utilização de várias threads a afectarem o UI, pq se alguma coisa correr mal, o debug é complicado.


Matraquilhos para Android.

Gratuito na Play Store.

https://play.google.com/store/apps/details?id=pt.bca.matraquilhos

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
msr

Obrigado pelas respostas e pelo código exemplo. Bastante explícito!

Já tenho isto a funcionar com o delegate.

Este caso é muito simples, tenho apenas 1 thread a correr. Mas já agora bruno1234, se fossem várias a afectarem o UI o que deveria fazer? Colocar um lock na chamada da função que altera o componente? Esse caso nao poderia criar situações de impasse?

Obrigado

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
bruno1234

Se fossem várias threads depende de como quisesses fazer.

O lock é uma solução, se for bem feito, n causa deadlocks, mas preferia optar por outra solução.

O que fazia era criar as threads para fazerem o seu trabalho, e devolviam o resultado para a thread principal q as lançou, e essa thread ficava então responsável por todas as alterações ao UI.


Matraquilhos para Android.

Gratuito na Play Store.

https://play.google.com/store/apps/details?id=pt.bca.matraquilhos

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
mjamado

bruno1234, como é que devolves o resultado de um thread à thread principal? Isso é novidade para mim, e olha que já ando no C# há muitos anos...

msr, o ónus da integridade dos dados está sempre do lado do programador. Quando estás a mexer em dados que precisem de ser lidos e escritos por várias threads ao mesmo tempo, o melhor mesmo é usar semáforos nas partes sensíveis, ou então montar um esquema em que cada thread mexa em pedaços separados.

Abraços!


"Para desenhar um website, não tenho que saber distinguir server-side de client-side" - um membro do fórum que se auto-intitula webdesigner. Temo pelo futuro da web.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
mjamado
Com um BackgroundWorker, por exemplo.

Fiquei pacientemente à espera que elaborasses; não o fizeste. Mas eu explico: o BackgroundWorker não "devolve" nada à thread principal, porque isso é impossível - aliás, o próprio conceito de assincronacidade não o permite. O que acontece é que é chamado um evento no final, cujo callback na thread principal vai buscar os dados finais, se forem passados. E isso podes fazer, também, na Thread, definindo o teu próprio evento.

O problema da integridade e consistência dos dados mantêm-se, visto que cada BW chamado irá disparar o evento e nada te garante que as threads acabem pela ordem com que foram chamadas. Na realidade, terás que garantir tu próprio a integridade e consistência dos dados no callback do RunWorkerCompleted...

O BackgroundWorker não é mais que uma Thread com eventos e com alguns settings já definidos (por exemplo, a Thread pode ser definida como foreground e o BackgroundWorker, como o próprio nome indica, não). E é mais pesada ao nível da instanciação (herda de Component, se não estou em erro).


"Para desenhar um website, não tenho que saber distinguir server-side de client-side" - um membro do fórum que se auto-intitula webdesigner. Temo pelo futuro da web.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.