Pregunta

Actualmente estoy trabajando en una aplicación que lee desde un archivo binario grande, que contiene varios miles de archivos, cada archivo está siendo procesado por alguna otra clase en la aplicación.Esta clase devuelve un objeto o un valor nulo.Quiero mostrar un progreso en el formulario principal pero por alguna razón no puedo entenderlo.

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);

El código anterior congela mi formulario, incluso si uso "TaskScheduler.FromCurrentSynchronizationContext", ¿por qué?Porque cuando uso el código debajo mi formulario se actualiza bien

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);           
    }
});

Soy nuevo en todo este flujo de datos de TPL, por lo que espero que alguien pueda compartir algo de luz sobre por qué en el segundo fragmento de código funciona y en el primero no.

Saludos cordiales, Martijn

¿Fue útil?

Solución

La razón por la que su interfaz de usuario está bloqueada es porque estas usando FromCurrentSynchronizationContext.Hace que el código se ejecute en el subproceso de la interfaz de usuario, lo que significa que se congelará si realiza alguna acción de ejecución prolongada (muy probablemente GetFileFromLargeFile()).

Por otro lado, hay que ejecutar el lblProgress.Text en el hilo de la interfaz de usuario.

No estoy seguro de que debas configurar lblProgress.Text directamente en este código, me parece un acoplamiento demasiado estrecho.Pero si quieres hacer eso, creo que deberías ejecutar solo esa línea en el hilo de la interfaz de usuario:

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);
    }
});

Pero una solución aún mejor sería si hicieras GetFileFromLargeFile() asincrónico y se aseguró de que no realice ninguna acción de larga duración en el subproceso de la interfaz de usuario (ConfigureAwait(false) te puede ayudar con eso).Si hiciste eso, el código del ActionBlock podría ejecutarse en el hilo de la interfaz de usuario sin congelar la interfaz de usuario:

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top