2011-09-14 15 views
12

Tengo una aplicación WinForm, y un conjunto observable de esta manera:cómo conseguir un contexto de sincronización WinForm o programa en un hilo WinForm

Form form = new Form(); 
Label lb = new Label(); 
form.Controls.Add(lb); 

Observable.Interval(TimeSpan.FromSeconds(1)) 
      .Subscribe(l => lb.Text = l.ToString()); 

Application.Run(form); 

Esto no funciona, ya que el l => lb.Text = l.ToString() no se ejecutará en el hilo principal que creó el formulario, pero no puedo averiguar cómo hacerlo funcionar en este hilo. Supongo que debería usar IObservable.SubscribeOn que toma IScheduler o SynchronizationContext, pero no sé cómo obtener el contexto de sincronización del hilo principal, y los únicos programadores que pude encontrar fueron las propiedades estáticas de Scheduler, como Scheduler.CurrentThread , Immediate, NewThread, TaskPool y ThreadPool, ninguno de los cuales funcionó.

Mi versión de Rx es 1.0.10621.

Respuesta

23

Justo después de que publico la pregunta, creo que la solución:

Form form = new Form(); 
Label lb = new Label(); 
form.Controls.Add(lb); 

Observable.Interval(TimeSpan.FromSeconds(2)) 
      .ObserveOn(SynchronizationContext.Current) 
      .Subscribe(l => lb.Text = l.ToString()); 

Application.Run(form); 

This enlace era muy útil. Dos notas:

  • No todos los hilos tienen un contexto de sincronización, pero la primera forma que se crea en un hilo establecerá un contexto de sincronización para ese hilo, por lo que el hilo de interfaz de usuario siempre tiene uno. El método correcto es ObserveOn, no SubscribeOn. Por el momento, no sé lo suficiente sobre esto para saber por qué, por lo que cualquier enlace en los comentarios sería apreciado.

edición: Gracias a la primera parte de this enlace, ahora entiendo más sobre la diferencia entre ObserveOn y SubscribeOn. En resumen:

  • un observable, que observa en un contexto de sincronización va a llamar a los métodos IObserver (OnNext y amigos) de ese contexto. En mi ejemplo, observo en el hilo principal/UI, por lo tanto no obtengo excepciones de hilos cruzados
  • SubscribeOn es un poco más complicado, así que aquí hay un ejemplo: Concat toma un número de observables y los combina con un observable largo . Una vez que un observable llama al OnCompleted, el observable combinado eliminará esa suscripción y se suscribirá al siguiente observable. Todo esto sucede en el hilo que llamó a OnCompleted, pero puede haber algunos problemas para suscribirse a observables, que fueron creados por Observable.FromEvent, p. Ej. Silverlight lanzará si agrega un controlador de eventos de otro subproceso que el subproceso de interfaz de usuario, y WinForms y WPF se lanzarán si agrega un controlador de eventos de múltiples subprocesos diferentes. SubscribeOn le permitirá controlar el hilo en el que su observable se conecta al evento subyacente.
+7

'SubscribeOn' solo establece qué hilo pasa la suscripción real, mientras que' ObserveOn' determina qué hilo se ejecutan las llamadas 'OnNext'. Para comparar con eventos, 'SubscribeOn' es como hacer que solo agregues manejadores de eventos en el hilo principal, pero' ObserveOn' se asegura de que la rutina de manejo de eventos sea llamada en el hilo correcto. –

+0

@Gideon: Gracias. Edité mi respuesta para reflejar mi comprensión mejorada, pero tus comentarios lo resumen todo muy bien. – Boris

+2

Si tiene una referencia a System.Reactive.Windows.Forms.dll, puede hacer .ObserveOn (formulario) y hará el equivalente a Control.Invocar. –