Как получить контекст синхронизации WinForm или расписание в потоке WinForm - PullRequest
12 голосов
/ 14 сентября 2011

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

23 голосов
/ 14 сентября 2011

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

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 и друзья) из этого контекста. В моем примере я наблюдаю за потоком main / UI, поэтому я не получаю исключений кросс-потока
  • SubscribeOn немного сложнее, поэтому вот пример: Concat берет несколько наблюдаемых и объединяет их в одну длинную наблюдаемую. Как только наблюдаемое вызывает OnCompleted, объединенная наблюдаемая избавится от этой подписки и подпишется на следующую наблюдаемую. Все это происходит в потоке, который называется OnCompleted, но могут быть некоторые проблемы с подпиской на наблюдаемые, которые были созданы Observable.FromEvent, например Silverlight сгенерирует, если вы добавите обработчик событий из другого потока, отличного от потока пользовательского интерфейса, а WinForms и WPF сгенерируют, если вы добавите обработчики событий из нескольких разных потоков. SubscribeOn позволит вам контролировать поток, в котором ваши наблюдаемые подключаются к базовому событию.
...