为什么这个Observable会阻塞WPF GUI线程(C#)?

3
给定:一个扩展方法,接受 Selenium IWebdriver 实例并返回 IObservable。
     public static IObservable<ObservableCollection<WebElementWrapper>> 
      GetAllElementsAsObservable(this IWebDriver wd)
      {
        return Observable.Create<ObservableCollection<WebElementWrapper>>(
             (IObserver<ObservableCollection<WebElementWrapper>> observer) =>
             {                     
                     var eles = wd.FindElements(By.CssSelector("*"));
                     var list = eles.ToWebElementObservableCollection();
                     observer.OnNext(list);
                     observer.OnCompleted();                     

                 return Disposable.Create(() => { });
             });
      }

以下是调用上述方法的代码(运行在GUI线程中)...

                //GUI Will Freeze on this call until OnCompleted is called
                cd.GetAllElementsAsObservable().Subscribe((WEWList) =>
                {
                    WebElementCollection = WEWList;
                    SetNavigationItems();
                });

能否有人帮我确定GUI线程被阻塞直到OnCompleted被调用的根本原因。如果我在第一个方法中使用Task.Run,我可以停止阻塞,但是这样我就必须将集合移回GUI线程。

这是否是因为GUI线程启动了Webdriver,而Observable正在使用它来提取元素导致的?

还是因为静态方法在GUI线程的启动时间被创建造成的?


如果你这样做 - Disposable.Create(() => { }) - 说明你正在做一些错误的操作。以你目前使用的方式,Observable.Create 是一个阻塞操作。.Create 内部的代码是订阅的一部分,但你在订阅期间完全运行了观察者,这就是为什么它是阻塞的。 - Enigmativity
2个回答

2
如果你这样做 - Disposable.Create(() => { }) - 说明你正在做一些错误的事情。使用Observable.Create的方式是阻塞操作。.Create中的代码是订阅的一部分,但你在订阅期间完成了观察者,所以它是阻塞的。

尝试像这样做:

public static IObservable<ObservableCollection<WebElementWrapper>>
    GetAllElementsAsObservable(this IWebDriver wd)
{
    return Observable.Create<ObservableCollection<WebElementWrapper>>(observer =>
        Observable
            .Start(() =>
                wd
                    .FindElements(By.CssSelector("*"))
                    .ToWebElementObservableCollection())
            .Subscribe(observer));
}

Observable.Defer(() => wd.FindElements(By.CssSelector("*")).ToWebElementObservableCollection()) 这样不是更简单吗? - Gluck
@Gluck - 不行,因为.ToWebElementObservableCollection()不会返回一个可观察对象。 - Enigmativity
好的,那就加一个额外的 Obs.Return 然后就能工作了。我有时总是试图避免中间订阅,这是我的错。谢谢! - Gluck

0

对于WPF,我也发现这两种方法可行。

SomeObservable
.SubscribeOn(Scheduler.Default)
.ObserveOn(Scheduler.CurrentThread)
.Subscribe(item => { //do something on gui thread here });

我不喜欢方法名字SubscribeOn,但是我这样认为...我希望可观察对象订阅某个调度程序。(我认为更好的命名应该是"ScheduleOn")。

ObserveOn方法名很合理。但请注意内置属性"Scheduler.Dispatcher"。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接