Actualizaciones de gráficos de UIThread de TPL BackgroundThread

Todo, tengo un Task that runs on a background thread and does some heavy lifting. The way I have it set up is that whilst this is being done, the UI thread updates the GUI with progress information, some of which use animated .gif files. Take a look at the following example code to illustrate the problem

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task<bool> task = Task.Factory.StartNew(() => 
{
    // Heavy work.
    if (heavyWorkCompleted)
        MethodToUpdateGui(uiScheduler); // Update DataGridView ImageCell on UI thread
    // More work.
}

I have a "In Progress" image that is shown in the appropriate DGV image cell during execution of "Heavy Work", if "Heavy Work" is successful this image changes to a tick (in DGV1), then MethodToUpdateGui(uiScheduler) uses this (just changed) image in DGV1 to update DGV2. However, when the MethodToUpdateGui(uiScheduler) runs it sometimes updates the DataGridView (DGV2) too fast. What I mean by this, is that the image is not the tick I want (and it should be due to successfull compleation) it is the "In Progress" image. It seems that the DGV is not updating fast enough for what I am doing.

Is there a better way to do this? Do I dare to spin the background thread after the GUI update to allow DGV1 to "catch-up" [this is nasty!]?

Gracias por su tiempo.

preguntado el 22 de mayo de 12 a las 18:05

1 Respuestas

Is there a better way to do this? Do I dare to spin the background thread after the GUI update to allow DGV1 to "catch-up" [this is nasty!]?

The issue here is that that TPL uses SynchronizationContext.Post to send the message to the UI thread. When the method runs, the Task will run asynchronously, which will, in turn, not update the GUI immediately.

If you want to make sure that the GUI is updated, the simplest option would be to have your MethodToUpdateGui block by waiting on the Task scheduled via uiScheduler.

Esto se vería algo así como:

void MethodToUpdateGui(TaskScheduler uiScheduler)
{
    var updateTask = Task.Factory.StartNew( () =>
    {
        // update gui
    }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

    // Block until Task completes...
    updateTask.Wait();
}

This is better than trying to spin, as it will block and get notified by the TPL automatically.

This is effectively doing Control.Invoke en lugar de Control.BeginInvoke, in Windows Forms terminology.

contestado el 22 de mayo de 12 a las 18:05

I actually do this. In all GUI updates using uiScheduler Yo siempre uso uiTask.Wait() (envuelto en un try/catch para atrapar cualquier AggregateExceptions of course), but this "racing" is still happening!? I am confused... - Caballero de la Luna

@Killercam Are you doing a "completion" message the same way? - Reed Copsey

The compleation message is handled with a continuation. I don't wait on the continuation, and the continuation delegate is invoked well after this problem arises. - Caballero de la Luna

@Killercam That sounds like something is preventing the UI from updating, since the continuation won't run until the UI thread is free... - Reed Copsey

That's an idea. I will check this tomorrow. Thanks very much for your help and time. - Caballero de la Luna

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.