12

Ho un'applicazione WinForm, e un set osservabile in questo modo:Come ottenere un contesto di sincronizzazione WinForm o un programma su un thread 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); 

Questo non funziona, dal momento che non viene eseguito il l => lb.Text = l.ToString() sul thread principale che ha creato il modulo, ma non riesco a capire come farlo funzionare su questo thread. Presumo, che dovrei usare IObservable.SubscribeOn che accetta uno IScheduler o uno SynchronizationContext, ma non so come ottenere il sincronizationcontext del thread principale, e gli unici Scheduler che ho trovato erano le proprietà statiche di Scheduler, come Scheduler.CurrentThread , Immediate, NewThread, TaskPool e ThreadPool, nessuno dei quali ha funzionato.

La mia versione Rx è 1.0.10621.

risposta

23

Subito dopo ho posto la domanda, trovo la soluzione:

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 collegamento è stato utile. Due note:

  • Non tutte le discussioni avere un contesto di sincronizzazione, ma la prima forma che viene creato su un thread fisserà un contesto sincronizzazione per quel filo, in modo che il filo dell'interfaccia utente ha sempre uno.
  • Il metodo corretto è ObserveOn, non SubscribeOn. Al momento, non ne so abbastanza per sapere perché, quindi qualsiasi link nei commenti sarebbe apprezzato.

edit: grazie alla prima parte del this collegamento, io ora capire di più circa la differenza tra ObserveOn e SubscribeOn. In breve:

  • un'osservabile, che osserva in un contesto di sincronizzazione chiamerà i metodi IObserver (OnNext e amici) da quel contesto. Nel mio esempio, osservo sul thread main/UI, quindi non ottengo eccezioni cross thread
  • SubscribeOn è un po 'più complicato, quindi ecco un esempio: Concat prende un numero di osservabili e li combina in un osservabile lungo . Una volta che un osservabile chiama lo OnCompleted, l'osservatore combinato eliminerà quell'abbonamento e si iscriverà al successivo osservabile. Tutto ciò si verifica nel thread che ha chiamato OnCompleted, ma ci possono essere alcuni problemi con la sottoscrizione a osservabili, che sono stati creati da Observable.FromEvent, ad es. Silverlight genererà se si aggiunge un gestore di eventi da un altro thread rispetto al thread dell'interfaccia utente e WinForms e WPF genereranno se si aggiungono gestori di eventi da più thread differenti. SubscribeOn ti consente di controllare il thread su cui il tuo osservatore si collega all'evento sottostante.
+7

"SubscribeOn" imposta solo il thread su cui si verifica la sottoscrizione effettiva, mentre "ObserveOn" determina quale thread vengono eseguite le chiamate "OnNext". Per il confronto con gli eventi, 'SubscribeOn' è come farti aggiungere solo gestori di eventi sul thread principale, ma' ObserveOn' si assicura che la routine di gestione degli eventi venga chiamata sul thread corretto. –

+0

@Gideon: Grazie. Ho modificato la mia risposta per riflettere la mia comprensione migliorata, ma i tuoi commenti riassumono tutto molto bene. – Boris

+2

Se si dispone di un riferimento a System.Reactive.Windows.Forms.dll, è possibile eseguire .ObserveOn (modulo) e farà l'equivalente di Control.Invoke. –