Threading e eventos do Windows Forms - ListBox é atualizado imediatamente, mas a barra de progresso sofre um grande atraso

StackOverflow https://stackoverflow.com/questions/12095

Pergunta

Nossa equipe está criando um novo sistema de fluxo de trabalho de recrutamento para substituir um antigo.Recebi a tarefa de migrar os dados antigos para o novo esquema.Decidi fazer isso criando um pequeno projeto do Windows Forms, pois os esquemas são radicalmente diferentes e os scripts TSQL diretos não são uma solução adequada.

A principal classe selada 'ImportController' que faz o trabalho declara o seguinte evento delegado:

public delegate void ImportProgressEventHandler(object sender, ImportProgressEventArgs e);
public static event ImportProgressEventHandler importProgressEvent;

A janela principal inicia um método estático nessa classe usando um novo thread:

Thread dataProcessingThread = new Thread(new ParameterizedThreadStart(ImportController.ImportData));
dataProcessingThread.Name = "Data Importer: Data Processing Thread";
dataProcessingThread.Start(settings);

os argumentos ImportProgressEvent carregam uma mensagem de string, um valor int máximo para a barra de progresso e um valor int de progresso atual.O formulário do Windows se inscreve no evento:

ImportController.importProgressEvent += new ImportController.ImportProgressEventHandler(ImportController_importProgressEvent);

E responde ao evento desta maneira usando seu próprio delegado:

    private delegate void TaskCompletedUIDelegate(string completedTask, int currentProgress, int progressMax);

private void ImportController_importProgressEvent(object sender, ImportProgressEventArgs e)
            {
                this.Invoke(new TaskCompletedUIDelegate(this.DisplayCompletedTask), e.CompletedTask, e.CurrentProgress, e.ProgressMax);
            }

Finalmente a barra de progresso e a caixa de listagem são atualizadas:

private void DisplayCompletedTask(string completedTask, int currentProgress, int progressMax)
        {
            string[] items = completedTask.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

            foreach (string item in items)
            {
                this.lstTasks.Items.Add(item);
            }

            if (currentProgress >= 0 && progressMax > 0 && currentProgress <= progressMax)
            {
                this.ImportProgressBar.Maximum = progressMax;
                this.ImportProgressBar.Value = currentProgress;
            }
        }

O problema é que o ListBox parece atualizar muito rapidamente, mas a barra de progresso nunca se move até que o lote esteja quase completo ???o que da ?

Foi útil?

Solução 4

@ John

Obrigado por as ligações.

@Will

Não há ganho de threadpooling como eu sei que sempre apenas desova uma vontade rosca. O uso de um fio é puramente para ter uma interface de usuário responsiva enquanto SQL Server está sendo bateu com leituras e gravações. Não é certamente uma rosca curta duração.

Em relação trenó-martelos você está certo. Mas, como se vê meu problema era entre a tela e cadeira depois de tudo. I parecem ter um lote unusal de dados que tem muitos muitos muitos registros chave mais estrangeiros do que os outros lotes e só acontece de ser selecionado no início do processo o que significa a currentProgress não fica ++ 'd para um bom 10 segundos.

@All

Obrigado por toda sua entrada, ele me fez pensar, o que me fez procurar outro lugar no código, o que levou ao meu momento ahaa de humildade onde eu provar mais uma vez o erro é geralmente humana:)

Outras dicas

Talvez você pode tentar o componente BackgroundWorker. Faz enfiar mais fácil. Exemplos aqui:

Talvez fora do escopo, mas, às vezes a sua utilidade para fazer um Application.DoEvents(); para fazer as peças gui reagir a entrada do usuário, como pressionar o botão de cancelar em um diálogo de status bar.

Você por acaso executar o Windows Vista? Eu observei o exatamente o mesmo em algumas aplicações relacionadas com o trabalho. De alguma forma, parece haver um atraso quando a barra de progresso "anima".

Você tem certeza de que o segmento interface do usuário está funcionando livremente durante todo este processo? ou seja, ele não está sentado bloqueado-up em um Junte-se ou alguma outra espera? Isso é o que parece para mim.

A sugestão de usar BackgroundWorker é um bom -. Definitivamente superior para tentar marreta o caminho para sair do problema com uma carga de chamadas de atualização / atualizar

E BackgroundWorker irá utilizar um segmento de pool, que é uma forma mais amigável para se comportar de criar o seu próprio segmento de curta duração.

Não há ganho de threadpooling como Eu sei que só vai gerar uma fio. O uso de um fio é puramente para ter uma interface de usuário responsiva enquanto SQL Servidor está sendo bateu com lê e escreve. Certamente não é um curto fio de duração.

OK, eu aprecio isso, e feliz que você encontrou o seu erro, mas você já olhou para BackgroundWorker? Ele faz muito bonito exatamente o que você está fazendo, mas de forma padronizada (ou seja, sem os seus próprios delegados) e sem a necessidade de criar um novo segmento -. Ambas as quais são (talvez pequeno, mas talvez ainda útil) vantagens

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top