2012-03-17 19 views
8

El siguiente código es una simplificación de un código en una aplicación real. El problema a continuación es que se ejecutará un trabajo largo en el hilo de la interfaz de usuario, en lugar de un hilo de fondo.Cómo hacer que una tarea NO se ejecute en el subproceso UI

void Do() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == true); 
     Task.Factory.StartNew(ShortUIWork, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    void ShortUIWork() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == true); 
     Task.Factory.StartNew(LongWork, TaskCreationOptions.LongRunning); 
    } 

    void LongWork() 
    { 
     Debug.Assert(this.Dispatcher.CheckAccess() == false); 
     Thread.Sleep(1000); 
    } 

So Do() se llama normalmente del contexto de la interfaz de usuario. Y también lo es ShortUIWork, tal como lo define TaskScheduler. Sin embargo, LongWork termina llamado también en el subproceso de interfaz de usuario, que, por supuesto, bloquea la interfaz de usuario.

¿Cómo asegurar que una tarea no se ejecute en el hilo de la interfaz de usuario?

+0

'tasks' crear unos hilos que * no * se ejecutan en la interfaz de usuario por definición. ¿Estás seguro de que 'LongWork' bloquea el hilo de UI? – Tigran

+2

@Tigran: TPL usa subprocesos de fondo por defecto, no por definición. –

+1

El código que no reproduce el problema no ayuda. Una razón común para un comportamiento como este es una clase contenedora para un componente COM. COM mantiene los objetos de las clases que se anuncian a sí mismos que no admiten ningún tipo de subprocesamiento seguro mediante la clasificación automática de todas las llamadas al subproceso que las creó. –

Respuesta

8

LongRunning es simplemente una pista para el TaskScheduler. En el caso del SynchronizationContextTaskScheduler (según lo devuelto por TaskScheduler.FromCurrentSynchronizationContext()), aparentemente ignora la sugerencia.

Por un lado esto parece contradictorio. Después de todo, si la tarea es de larga ejecución, es poco probable que desee que se ejecute en el hilo de la interfaz de usuario. Por otra parte, de acuerdo con MSDN:

LongRunning - Especifica que la tarea será una larga ejecución, operación de grano grueso. Proporciona una pista al TaskScheduler que puede estar justificada por la sobresuscripción .

Desde el hilo de interfaz de usuario no es un hilo de rosca de la piscina, hay "exceso de suscripción" (subproceso del grupo de inanición) puede ocurrir, por lo tanto tiene sentido que la pista no tendrá ningún efecto para el SynchronizationContextTaskScheduler.

Independientemente, se puede evitar el problema al cambiar de nuevo al programador de tareas por defecto:

void ShortUIWork() 
{ 
    Debug.Assert(this.Dispatcher.CheckAccess() == true); 
    Task.Factory.StartNew(LongWork, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); 
} 
+0

¿Por qué el 'Debug.Assert (this.Dispatcher.CheckAccess() == falso);' en el pase 'LongWork', aunque se ejecuta en el hilo de la interfaz de usuario? –

+0

@Branko: no sigo. LongWork no se ejecuta en el hilo de la interfaz de usuario si el código se cambia según mi respuesta. Es por eso que la afirmación pasa (pero falla con el código original). –

+0

Extraño, podría haber jurado que pasó cuando originalmente lo depuré pero ahora falla (en el código original). O he golpeado alguna extraña interacción con el depurador (sea lo que sea) o esto es solo una falla cerebral de mi parte;) Mis disculpas. –

Cuestiones relacionadas