hel*_*ker 4 c# wpf asynchronous scheduler reactiveui
我很难找到在我的 ViewModel 中安排长时间运行的反应性属性“getter”的正确方法。
RX 简介的这段摘录准确地描述了我想要做的事情:
- 响应某种用户操作
- 在后台线程上工作
- 将结果传回 UI 线程
- 更新用户界面
只有在这种情况下,除了用户交互之外,我才想对其他属性的变化做出反应。
下面是我用来从原始属性获取派生属性的通用模板(在实际代码中,有级联派生属性链)。
在 Reactive ViewModel(从 ReactiveObject 继承)中,我已经有一些从其他属性派生的属性。例如,当Original发生变化时,Derived将重新计算。
public TOriginal Original
{
get { return _original; }
set { this.RaiseAndSetIfChanged(ref _original, value); }
}
TOriginal _original;
public TDerived Derived { get { return _derived.Value; } }
readonly ObservableAsPropertyHelper<double[,]> _derived;
this.WhenAnyValue(x => x.Original)
.Where(originalValue => originalValue != null)
// ObserveOn? SubscribeOn? Which scheduler?
.Select(derivedValue => LongRunningCalculation(originalValue))
// Same thing here: ObserveOn? SubscribeOn? Which scheduler?
.ToProperty(this, x => x.Derived, out _derived); // should I use the `scheduler:` in this method?
Run Code Online (Sandbox Code Playgroud)
我的问题是:我不知道应该如何组合这些不同的“设计选择”来获得我想要的响应式 UI:
RxApp.TaskpoolScheduler,RxApp.MainThreadScheduler,NewThreadScheduler.Default,以及其它可能的。SubscribeOnVSObserveOn甚至ObserveOnDispatcher还是scheduler:参数ToProperty?Select操作员之前和之后,但我不太确定。Select坦率地说,我不确定甚至需要。Binding.IsAsync到true,但我尝试过了,没有显得太大的区别,但同样,或许是因为其他因素。SynchronizationContext和ThreadPriority相关的概念在这里吗?有没有办法在显示的代码中配置它们?ReactiveCommand为此使用或其他一些 ReactiveUI 类吗?最令人伤脑筋的事实是,对于某些组合,计算正常运行,但会阻塞 UI,而对于其他一些组合,值是异步计算的,UI 的阻塞较少,但有时会部分导出值(例如,在项目集合中)不可用!
对不起,如果我要求太多,但我没有找到任何权威的预期方法来做我在文档中需要的东西。
在 Rx.NET 中有一些调度程序,包括一个专用于 WPF 的特殊调度程序。
TaskPoolScheduler在任务池上运行代码。这有点像在Task.NewThreadScheduler产生一个新线程来运行代码。通常不要使用这个操作符,除非你知道你“需要”它(你几乎从来不需要)DispatcherScheduler在 UI 线程上运行代码。当您要在 VM 中设置属性时使用此选项RxUI 带来了两个平台无关的调度程序抽象。无论您使用什么平台(WPF、UWP、Xamarin.iOS、Xamarin.Android),RxApp.MainThreadScheduler都将始终指代 UI 线程调度程序,而RxApp.TaskPoolScheduler指代类似于后台线程的东西。
如果您想保持简单,只需使用RxApp调度程序;RxApp.MainThreadScheduler用于 UI 的东西和RxApp.TaskPoolScheduler背景/重型的东西。
这个名字SubscribeOn()有点混乱,因为它不直接影响Subscribe()方法。SubscribeOn()决定 observable 将在哪个调度器上启动;原始/第一个订阅将在哪个调度程序上完成(而不是该Subscribe()方法将在哪个调度程序上执行)。我喜欢认为SubsribeOn()将 observable 链向上移动到顶部,并确保 observable 在给定的调度程序上产生值。
一些操作符让你指定他们应该在哪个调度程序上运行。当他们这样做时,你应该总是更喜欢传递一个 scheduler,这样你就知道他们要去哪里工作并防止他们潜在地阻塞 UI thead(尽管他们不应该)。SubsribeOn()对 observable 来说是一种“黑客”,它不允许您指定调度程序。如果您使用SubscribeOn(),但运算符指定了调度程序,则来自运算符的信号将在运算符调度程序上发出,而不是您在 中指定的调度程序SubscribeOn()。
ObserveOn()与 几乎相同SubscribeOn(),但它“从现在开始”。后面的运算符和代码ObserveOn()将在给定的调度程序上执行ObserveOn()。我喜欢认为这ObserveOn()意味着“将线程更改为这个”。
如果您要进行繁重的工作,请将其放入一个函数中并调用该函数,就像您对LongRunningCalculation(). 你可以在它ObserveOn(RxApp.TaskPoolScheduler)之前Select()和ObserveOn(RxApp.MainThreadScheduler之后使用 put an ,但我更喜欢Observable.Start()结合使用SelectMany()。
Observable.Start()基本上是Observable.Return()用于函数:“给我这个函数的结果作为一个可观察的。” 您还可以指定它应该调用该函数的调度程序。
SelectMany()确保我们得到 observable 的结果,而不是 observable 本身。(这有点像awaitfor observables:“在我们得到这个 observable 的结果之前不要执行这个 next 操作符”)
您正在正确执行派生属性。
使用WhenAnyValue()获得的财产和管道的是一个变化ToProperty()。您放置在两者之间的运算符可能会在后台线程上工作,从而延迟派生属性的设置,但这就是为什么我们有INotifyPropertyChanged.
以下是我将如何实现您的具体示例:
public TOriginal Original
{
get { return _original; }
set { this.RaiseAndSetIfChanged(ref _original, value); }
}
TOriginal _original;
public TDerived Derived { get { return _derived.Value; } }
readonly ObservableAsPropertyHelper<double[,]> _derived;
_derived = this.WhenAnyValue(x => x.Original)
.Where(originalValue => originalValue != null)
// Sepcify the scheduler to the operator directly
.SelectMany(originalValue =>
Observable.Start(
() => LongRunningCalculation(originalValue),
RxApp.TaskPoolScheduler))
.ObserveOn(RxApp.MainThreadScheduler)
// I prefer this overload of ToProperty, which returns an ObservableAsPropertyHelper
.ToProperty(this, x => x.Derived);
Run Code Online (Sandbox Code Playgroud)
我们有一个 ReactiveUI 的 Slack 团队,欢迎您加入。您可以通过单击此处请求邀请
| 归档时间: |
|
| 查看次数: |
1255 次 |
| 最近记录: |