通过呼叫顺序跨多个Rx主题保证订购?

时间:2015-04-01 17:07:08

标签: c# system.reactive

在我下面的示例中,我有2个主题,_primarySubject总是在_secondarySubject之前调用,订阅者保证在次要回调之前接收主要回调?测试简单的测试似乎是肯定的,或者这是侥幸吗?

如果这是侥幸的话,我怎么能改变代码来保证订单,就像我在这里描述的那样。

非常感谢。

public class EventGenerator
{
    ISubject<string> _primarySubject = new Subject<string>();
    ISubject<int> _secondarySubject = new Subject<int>();
    private int i;
    public void SendEvent(string message)
    {
        _primarySubject.OnNext(message);
        _secondarySubject.OnNext(i++);
    }

    public IObservable<string> GetPrimaryStream()
    {
        return _primarySubject;
    }

    public IObservable<int> GetSecondaryStream()
    {
        return _secondarySubject;
    }
}

public class EventSubscriber
{
    private static IScheduler StaticFakeGUIThread = new EventLoopScheduler(x => new Thread(x) { Name = "GUIThread" });

    private readonly int _id;
    public EventSubscriber(int id, EventGenerator eventGenerator)
    {
        _id = id;
        eventGenerator.GetPrimaryStream().ObserveOn(StaticFakeGUIThread).Subscribe(OnPrimaryEvent);
        eventGenerator.GetSecondaryStream().ObserveOn(StaticFakeGUIThread).Subscribe(OnSecondaryEvent);
    }

    private void OnPrimaryEvent(String eventMsg)
    {
        string msg = eventMsg;
    }

    private void OnSecondaryEvent(int i)
    {
        int msg = i;
    }
}

[TestFixture]
public class EventGeneratorTest
{
    [Test]
    public void Test()
    {
        EventGenerator eg = new EventGenerator();

        EventSubscriber s1 = new EventSubscriber(1,eg);
        EventSubscriber s2 = new EventSubscriber(2, eg);

        eg.SendEvent("A");
        eg.SendEvent("B");
    }
}

3 个答案:

答案 0 :(得分:1)

通常,Rx对不同可观察流的相对排序没有很多保证。订购取决于RX无法控制的许多因素。

在您的具体情况下,保证订购:

  1. 由于您按顺序同步触发主题,因此可以保证按顺序触发其即时订阅(.ObserveOn(StaticFakeUIThread))。
  2. 由于您对ObserveOn的两次调用都使用相同的调度程序实例,因此可以保证在观察辅助流事件之前调度主流事件的观察结果,并且将使用相同的调度程序
  3. 由于您的计划程序是EventLoopScheduler,因此可以保证它不会同时运行计划任务,而是按照计划的顺序运行它们。
  4. 因此,您的观察确实会按顺序进行。

    EventLoopScheduler替换为TaskPoolSchedulerThreadPoolSchedulerScheduler.Default,并且将不再保证两个流的相对排序,因为这些调度程序被允许同时运行两个计划任务。

答案 1 :(得分:1)

这完全取决于你所说的“保证”。 Rx的当前实现和编写的代码使得调用顺序始终如您所观察到的那样 - 但是,没有已发布的规则来管理该行为并保证它。

编写代码的方式对使用两个主题没有特别的好处 - 或者实际上是两个订阅,因为处理程序是在同一个线程上调用的。只需使用组合事件的单个主题,您就可以获得所需的保证 - 为此使用自定义类型。为了方便起见,我使用了一个元组:

ISubject<Tuple<string,int>> _subject = new Subject<Tuple<string,int>>();
private int i;
public void SendEvent(string message)
{
    _subject.OnNext(Tuple.Create(message,i++));
}

您的订阅:

private void OnEvent(Tuple<String,int> eventMsg)
{
    string msgText = eventMsg.Item1;
    int msgNum = eventMsg.Item2;
}

无论调度程序如何,这都可以为您提供保证。我怀疑您的问题必须有一些更有说服力的理由 - 否则这会更有效。

如果您只是想使用计数器btw,那么Select的重载会为您引入基于0的事件索引计数器。

答案 2 :(得分:0)

理论上,一切都应该按顺序运作。实际上,EventLoopScheduler中存在一个错误,如果队列正在挨饿,它会破坏顺序。

在此处查看问题 https://github.com/Reactive-Extensions/Rx.NET/issues/455