Как получить контекст синхронизации WinForm или расписание в потоке WinForm

У меня есть приложение 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);

Это не работает, так как l => lb.Text = l.ToString() не будет запускаться в основном потоке, который создал форму, но я не могу понять, как заставить его работать в этом потоке. Я предполагаю, что я должен использовать IObservable.SubscribeOn, который принимает либо IScheduler, либо SynchronizationContext, но я не знаю, как получить синхронный текст основного потока, и единственными планировщиками, которые я смог найти, были статические свойства Scheduler, например Scheduler.CurrentThread, Immediate, NewThread, TaskPool и ThreadPool, ни одна из которых не работала.

Моя версия Rx 1.0.10621.

Ответ 1

Сразу после публикации вопроса я найду решение:

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);

Эта ссылка была полезной. Две заметки:

  • Не все потоки имеют контекст синхронизации, но первая форма, созданная в потоке, установит контекст синхронизации для этого потока, поэтому поток пользовательского интерфейса всегда имеет один.
  • Правильный метод ObserveOn, а не SubscribeOn. На данный момент я не знаю достаточно об этом, чтобы знать, почему, поэтому любые ссылки в комментариях будут оценены.

edit: Благодаря первой части этой ссылки я теперь больше понимаю разницу между ObserveOn и SubscribeOn. Короче говоря:

  • Наблюдаемый, наблюдаемый в контексте синхронизации, вызовет методы IObserver (OnNext и друзей) из этого контекста. В моем примере я наблюдаю за потоком основного/пользовательского интерфейса, поэтому поэтому я не вижу исключений для перекрестных потоков
  • SubscribeOn немного сложнее, поэтому вот пример: Concat берет ряд наблюдаемых и объединяет их в один длинный наблюдаемый. Как только наблюдаемые вызовы OnCompleted, объединенные наблюдаемые будут распоряжаться этой подпиской и подписаться на следующее наблюдаемое. Все это происходит в потоке, который называется OnCompleted, но могут возникнуть проблемы с подпиской на наблюдаемые, которые были созданы Observable.FromEvent, например. Silverlight будет бросать, если вы добавите обработчик событий из другого потока, кроме потока пользовательского интерфейса, а WinForms и WPF будут метаться, если вы добавите обработчики событий из нескольких разных потоков. SubscribeOn позволит вам контролировать поток, на котором ваши наблюдаемые привязки попадают в основное событие.