2011-11-19 11 views
6

Recibo una excepción en tiempo de ejecución al intentar ejecutar el ejemplo a continuación.TaskScheduler.FromCurrentSynchronizationContext() en .NET

Unhandled Exception: System.InvalidOperationException: The current SynchronizationContext may not be used as a TaskScheduler. 
    at System.Threading.Tasks.SynchronizationContextTaskScheduler..ctor() 
    at System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext() 
    at TaskDemo.MyForm..ctor() in D:\myStudio\ASPNet\CSharp\CSharp4\MyApp\MyApp\Hello.cs:line 428 
    at TaskDemo.SynchronizationContextTaskScheduler() in D:\myStudio\ASPNet\CSharp\CSharp4\MyApp\MyApp\Hello.cs:line 396 
    at TaskDemo.Go() in D:\myStudio\ASPNet\CSharp\CSharp4\MyApp\CLRviaCSharp\Hello.cs:line 214 
    at ComputeOps.Main() in D:\myStudio\ASPNet\CSharp\CSharp4\MyApp\CLRviaCSharp\Hello.cs:line 23 

Ejemplo de código:

public class TaskSchedulerTest { 

    public void Test() { 
     SynchronizationContextTaskScheduler(); 
    } 

    private void SynchronizationContextTaskScheduler() { 
     var f = new MyForm(); 
     System.Windows.Forms.Application.Run(); 
    } 

    private sealed class MyForm : System.Windows.Forms.Form { 
     public MyForm() { 
      Text = "Synchronization Context Task Scheduler Demo"; 
      Visible = true; Width = 400; Height = 100; 
     } 

     private readonly TaskScheduler m_syncContextTaskScheduler = 
      TaskScheduler.FromCurrentSynchronizationContext(); 

     private CancellationTokenSource m_cts; 

     protected override void OnMouseClick(System.Windows.Forms.MouseEventArgs e) { 
      if (m_cts != null) { // An operation is in flight, cancel it 
       m_cts.Cancel(); 
       m_cts = null; 
      } else {    // An operation is not in flight, start it 
       Text = "Operation running"; 
       m_cts = new CancellationTokenSource(); 

       // This task uses the default task scheduler and executes on a thread pool thread 
       var t = new Task<Int32>(() => Sum(m_cts.Token, 20000), m_cts.Token); 
       t.Start(); 

       // These tasks use the synchronization context task scheduler and execute on the GUI thread 
       t.ContinueWith(task => Text = "Result: " + task.Result, 
        CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, 
        m_syncContextTaskScheduler); 

       t.ContinueWith(task => Text = "Operation canceled", 
        CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, 
        m_syncContextTaskScheduler); 

       t.ContinueWith(task => Text = "Operation faulted", 
        CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, 
        m_syncContextTaskScheduler); 
      } 
      base.OnMouseClick(e); 
     } 
    } 
} 

Alguna idea?

Respuesta

7

pon la creación de m_syncContextTaskScheduler en tu constructor de formularios.

public MyForm() { 
    Text = "Synchronization Context Task Scheduler Demo"; 
    Visible = true; Width = 400; Height = 100; 
    m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
} 
+0

Gracias! Funciona, ¿cuál es la razón detrás de esto? – Pingpong

+3

Porque 'FromCurrentSynchronizationContext()' no estaba en un contexto válido durante la inicialización de la variable (debe estar en el hilo de la IU). Ah, y acepte la respuesta, anima a la gente a que lo ayude :) – Alex

6

Gracias! Funciona, ¿cuál es la razón detrás de esto?

Debido a que el constructor inicializa los de sólo lectura miembros que tienen un inicializador demasiado pronto. El constructor de la clase Form instala el proveedor de sincronización si es necesario, una instancia de una clase llamada WindowsFormsSynchronizationContext. El compilador de C# genera el código para los inicializadores de solo lectura antes de llamando al constructor de la clase base. Mover la asignación al cuerpo constructor asegura que se inicialice después de llamando al constructor base.

Ten cuidado con readonly inicializadores de miembros, mantenlos sencillos.

+0

Gracias por la explicación. – Pingpong