C# ler生成一个新线程来运行代码。通常不要使用这个操作符,除非你知道你“需要”它(你几乎从不需要) DispatchersScheduler在UI线程上运行代码。在VM中设置属性时使用此选项

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

RxUI带来了两种平台无关的调度器抽象。无论您在哪个平台(WPF、UWP、Xamarin.iOS、Xamarin.Android)
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);