C# ler生成一个新线程来运行代码。通常不要使用这个操作符,除非你知道你“需要”它(你几乎从不需要) DispatchersScheduler在UI线程上运行代码。在VM中设置属性时使用此选项
RxUI带来了两种平台无关的调度器抽象。无论您在哪个平台(WPF、UWP、Xamarin.iOS、Xamarin.Android)C# ler生成一个新线程来运行代码。通常不要使用这个操作符,除非你知道你“需要”它(你几乎从不需要) DispatchersScheduler在UI线程上运行代码。在VM中设置属性时使用此选项,c#,wpf,asynchronous,scheduler,reactiveui,C#,Wpf,Asynchronous,Scheduler,Reactiveui,RxUI带来了两种平台无关的调度器抽象。无论您在哪个平台(WPF、UWP、Xamarin.iOS、Xamarin.Android)RxApp.MainThreadScheduler将始终引用UI线程调度程序,而RxApp.TaskPoolScheduler将引用类似于后台线程的内容 如果您想保持简单,只需使用RxApp调度器RxApp.MainThreadScheduler用于UI内容,而RxApp.TaskPoolScheduler用于后台/重载内容 观察/订阅 名称SubscribeOn()
RxApp.MainThreadScheduler
将始终引用UI线程调度程序,而RxApp.TaskPoolScheduler
将引用类似于后台线程的内容
如果您想保持简单,只需使用RxApp
调度器RxApp.MainThreadScheduler
用于UI内容,而RxApp.TaskPoolScheduler
用于后台/重载内容
观察/订阅
名称SubscribeOn()
有点混乱,因为它不会直接影响Subscribe()
方法SubscribeOn()
决定可观察对象将在哪个计划程序上启动;原始/第一次订阅将在哪个计划程序上执行(而不是Subscribe()
方法将在哪个计划程序上执行)。我喜欢认为subscribeon()
将可观察链上移到顶部,并确保可观察链在给定的调度程序上生成值
有些操作符让您指定它们应该在哪个调度器上运行。当他们这样做时,您应该始终希望传递一个调度程序,这样您就知道他们将在哪里工作,并防止他们潜在地阻塞UI thead(尽管他们不应该这样做)subscribeon()
是一种不允许指定调度程序的可观察对象的“黑客”。如果使用SubscribeOn()
,但操作员指定了一个调度程序,则来自操作员的信号将在操作员调度程序上发出,而不是在SubscribeOn()
中指定的信号
ObserveOn()
的功能与SubscribeOn()
的功能大致相同,但它是“从这一点开始”的。ObserveOn()
后面的运算符和代码将在指定给ObserveOn()
的调度程序上执行。我喜欢认为ObserveOn()
的意思是“将线程更改为此线程”
做繁重的工作
如果您要做繁重的工作,请将其放入函数中并调用该函数,就像您使用LongRunningCalculation()
所做的那样。您可以在Select()
之前放置一个ObserveOn(RxApp.TaskPoolScheduler)
,在它之后放置一个ObserveOn(RxApp.MainThreadScheduler
),但我更喜欢将Observable.Start()
与SelectMany()
结合使用
Observable.Start()
基本上是Observable.Return()
函数:“将此函数的结果作为一个Observable提供给我。”您还可以指定它应该调用函数的调度程序
SelectMany()
确保我们得到的是可观察的结果,而不是可观察的本身。(这有点像等待
可观察的结果:“在我们得到该可观察的结果之前,不要执行下一个操作符”)
派生属性
您正在正确执行派生属性
使用WhenyValue()
获取属性的更改,并将其导入到TopProperty()
中。在这两者之间插入的运算符可能会在后台线程上执行操作,从而延迟派生属性的设置,但这就是为什么我们更改了InotifyProperty
我的看法
下面是我将如何实现您的具体示例:
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);
公共原始版本
{
获取{return\u original;}
设置{this.RaiseAndSetIfChanged(ref_original,value);}
}
原始的;
公共TDerived派生{get{return}
只读ObservablesPropertyHelper\u派生;
_派生=此.whenyValue(x=>x.Original)
.Where(originalValue=>originalValue!=null)
//将调度程序直接指定给操作员
.SelectMany(原始值=>
可观察,开始(
()=>LongRunningCalculation(原始值),
RxApp.TaskPoolScheduler)
.ObserveOn(RxApp.MainThreadScheduler)
//我更喜欢TopProperty的这个重载,它返回一个ObservablesPropertyHelper
.ToProperty(此,x=>x.Derived);
我们有一个反应UI的松弛团队,欢迎您加入。您可以通过单击请求邀请,而不是第一个观察者使用SubscribeOn将其推到另一个线程上,这不是更好吗?SubscribeOn很少使用。只有在收到第一个事件之前,订阅行为做了一些奇怪的事情时才有意义。大多数情况下,您只使用ObserveOn。这是为了获取更多信息。在
Select
之前和之后使用SubscribeOn
使UI的工作与我预期的非常相似。但使用最初建议的ObserveOn
会阻止UI。您可以始终检查正在进行工作的线程。设置断点,然后检查t他打开了窗口。如果你的工作仍然在主线程上,那么你做错了什么。你应该发布你尝试过的确切代码。关于“解决我的问题”与“理解正在发生的事情”,尽管我对SubscribeOn
备选方案的响应性很好,但我觉得自己并没有比以前更好。非常感谢您的回答,这是非常有启发性的。一般来说,回答不错,除了。我喜欢认为subscribe()将observable链上移到顶部,确保observable在给定的调度程序上生成值。
我在回答中给出了一组测试用例,这些测试用例表明,通常情况下这是不正确的,即使在某些情况下看起来是这样。你是对的。如果操作员使用另一个调度程序或创建新线程(如测试中所示),它将在调度程序/线程上发出信号。我已更新了答案,但您的测试要详细得多。
this.WhenAnyValue(x => x.Original)
.Where(originalValue => originalValue != null)
.ObserveOn(TaskPoolScheduler.Default)
.Select(derivedValue => LongRunningCalculation(originalValue))
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.Derived, out _derived);
public static IObservable<TSource>
SubscribeOn<TSource>
( IObservable<TSource> source
, IScheduler scheduler
)
{
if (source == null)
throw new ArgumentNullException("source");
if (scheduler == null)
throw new ArgumentNullException("scheduler");
return (IObservable<TSource>) new AnonymousObservable<TSource>((Func<IObserver<TSource>, IDisposable>) (observer =>
{
SingleAssignmentDisposable assignmentDisposable = new SingleAssignmentDisposable();
SerialDisposable d = new SerialDisposable();
d.Disposable = (IDisposable) assignmentDisposable;
assignmentDisposable.Disposable = scheduler.Schedule((Action) (() => d.Disposable = (IDisposable) new ScheduledDisposable(scheduler, source.SubscribeSafe<TSource>(observer))));
return (IDisposable) d;
}));
}
using System;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace SubscribeOnVsObserveOn
{
class Program
{
static readonly Subject<object> EventsSubject = new Subject<object>();
private static readonly IObservable<object> Events = Observable.Create<object>
( observer =>
{
Info( "Subscribing" );
return EventsSubject.Subscribe( observer );
} );
public static void Info(string msg)
{
var currentThread = Thread.CurrentThread;
var currentThreadName = string.IsNullOrWhiteSpace( currentThread.Name ) ? "<no name>" : currentThread.Name;
Console.WriteLine
( $"Thread Id {currentThread.ManagedThreadId} {currentThreadName} - " + msg );
}
public static void Foo()
{
Thread.CurrentThread.Name = "Main Thread";
Info( "Starting" );
void OnNext(object o) => Info( $"Received {o}" );
void Notify(object obj)
{
Info( $"Sending {obj}" );
EventsSubject.OnNext( obj );
}
void StartAndSend(object o, string threadName)
{
var thread = new Thread(Notify);
thread.Name = threadName;
thread.Start(o);
thread.Join();
}
Notify(1);
Console.WriteLine("=============================================" );
Console.WriteLine("Subscribe Only" );
Console.WriteLine("=============================================" );
using (Events.Subscribe(OnNext))
{
Thread.Sleep( 200 );
StartAndSend(2, "A");
StartAndSend(3, "B");
}
Console.WriteLine("=============================================" );
Console.WriteLine("Subscribe With SubscribeOn(CurrentThreadScheduler)" );
Console.WriteLine("=============================================" );
using (Events.SubscribeOn( CurrentThreadScheduler.Instance ).Subscribe(OnNext))
{
Thread.Sleep( 200 );
StartAndSend(2, "A");
StartAndSend(3, "B");
}
Console.WriteLine("=============================================" );
Console.WriteLine("Subscribe With SubscribeOn(ThreadPool)" );
Console.WriteLine("=============================================" );
using (Events.SubscribeOn( ThreadPoolScheduler.Instance ).Subscribe(OnNext))
{
Thread.Sleep( 200 );
StartAndSend(2, "A");
StartAndSend(3, "B");
}
Console.WriteLine("=============================================" );
Console.WriteLine("Subscribe With SubscribeOn(NewThread)" );
Console.WriteLine("=============================================" );
using (Events.SubscribeOn( NewThreadScheduler.Default ).Subscribe(OnNext))
{
Thread.Sleep( 200 );
StartAndSend(2, "A");
StartAndSend(3, "B");
}
Console.WriteLine("=============================================" );
Console.WriteLine("Subscribe With SubscribeOn(NewThread) + ObserveOn" );
Console.WriteLine("=============================================" );
using (Events.SubscribeOn( NewThreadScheduler.Default ).ObserveOn(TaskPoolScheduler.Default ).Subscribe(OnNext))
{
Thread.Sleep( 200 );
StartAndSend(2, "A");
StartAndSend(3, "B");
}
}
static void Main(string[] args)
{
Foo();
Console.WriteLine( "Press Any Key" );
Console.ReadLine();
}
}
}
Thread Id 1 Main Thread - Starting
Thread Id 1 Main Thread - Sending 1
=============================================
Subscribe Only
=============================================
Thread Id 1 Main Thread - Subscribing
Thread Id 4 A - Sending 2
Thread Id 4 A - Received 2
Thread Id 5 B - Sending 3
Thread Id 5 B - Received 3
=============================================
Subscribe With SubscribeOn(CurrentThreadScheduler)
=============================================
Thread Id 1 Main Thread - Subscribing
Thread Id 6 A - Sending 2
Thread Id 6 A - Received 2
Thread Id 7 B - Sending 3
Thread Id 7 B - Received 3
=============================================
Subscribe With SubscribeOn(ThreadPool)
=============================================
Thread Id 8 <no name> - Subscribing
Thread Id 10 A - Sending 2
Thread Id 10 A - Received 2
Thread Id 11 B - Sending 3
Thread Id 11 B - Received 3
=============================================
Subscribe With SubscribeOn(NewThread)
=============================================
Thread Id 12 <no name> - Subscribing
Thread Id 13 A - Sending 2
Thread Id 13 A - Received 2
Thread Id 14 B - Sending 3
Thread Id 14 B - Received 3
=============================================
Subscribe With SubscribeOn(NewThread) + ObserveOn
=============================================
Thread Id 16 <no name> - Subscribing
Thread Id 17 A - Sending 2
Thread Id 19 B - Sending 3
Thread Id 18 <no name> - Received 2
Thread Id 18 <no name> - Received 3
Press Any Key
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);