Pergunta

Eu trabalho atual um aplicativo que lê de um arquivo binário grande, que contém muitos milhares de arquivos, cada arquivo que está sendo processado por alguma outra classe na aplicação.Esta classe retorna um objeto ou nulo.Eu quero mostrar um progresso no formulário principal, mas por algum motivo eu não posso colocar minha cabeça em torno dela.

int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       TotalFound++;
       buffer.post(file);
       lblProgress.Text = String.Format("{0}", TotalFound);
    }
}, options);

O código acima congela meu Formulário, mesmo eu uso "TaskScheduler.FromCurrentSynchronizationContext", por quê?Porque quando eu uso o código abaixo minha forma de atualizações de multa

DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, }; 
var producer = new ActionBlock<someObject>(largeFile=>{ 
    var file = GetFileFromLargeFile(largeFile);
    if(file !=null){
       Task.Factory.StartNew(() => {
          TotalFound++;
          buffer.Post(file);
       }).ContinueWith(uiTask => {
          lblProgress.Text = String.Format("{0}", TotalFound);
       },CancellationToken.None, TaskContinuationOptions.None, uiScheduler);           
    }
});

Eu sou novo para isso todo o fluxo de dados de TPL, então eu estou esperando que alguém poderia compartilhar alguma luz sobre por que no segundo trecho de código que funciona e o primeiro trecho não.

Com os melhores cumprimentos, Martijn

Foi útil?

Solução

A razão de sua INTERFACE do usuário é bloqueada é becase você está usando FromCurrentSynchronizationContext.Ele faz com que o código a ser executado no thread da INTERFACE do usuário, o que significa que ele irá congelar se você está fazendo algum tempo a executar a ação (o mais provável GetFileFromLargeFile()).

Por outro lado, você tem que executar o lblProgress.Text no thread da INTERFACE do usuário.

Eu não tenho certeza se você deve definir lblProgress.Text diretamente neste código, parecer apertado demais para atrelado para mim.Mas se você quiser fazer isso, eu acho que você deve executar apenas essa linha no thread da INTERFACE do usuário:

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        await Task.Factory.StartNew(
            () => lblProgress.Text = String.Format("{0}", TotalFound),
            CancellationToken.None, TaskCreationOptions.None, uiScheduler);
    }
});

Mas mesmo a melhor solução seria, se você fez GetFileFromLargeFile() assíncrona e a certeza de que ele não faz nenhuma longo execução de ações no thread da INTERFACE do usuário (ConfigureAwait(false) pode te ajudar com isso).Se você fez isso, o código do ActionBlock poderia ser executado no thread da INTERFACE do usuário sem congelamento sua INTERFACE do usuário:

var producer = new ActionBlock<someObject>(async largeFile =>
{
    var file = await GetFileFromLargeFile(largeFile);
    if (file != null)
    {
        TotalFound++;
        await buffer.SendAsync(file);
        lblProgress.Text = String.Format("{0}", TotalFound)
    }
}, options);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top