MultiCast和订阅主题的反应性框架问题

时间:2013-04-03 00:27:29

标签: c# system.reactive

我刚刚开始学习如何使用Reactive Framework,并且很难将多播发布到多个订阅者。

我让一切都运转正常:

m_MessagePublisher = m_ServerClient.MessageQueue
      .GetConsumingEnumerable()
      .ToObservable(TaskPoolScheduler.Default);

var genericServerMessageSubscriber = m_MessagePublisher
      .Where(message => message is GenericServerMessage)
      .Subscribe(message =>
      {
          // do something here
      }

但后来我意识到这不支持多播,当我试图连接另一个应该被相同消息命中的用户时,它不会被激活。我一直在阅读.MultiCast扩展并尝试弄清楚Subject是如何进行此操作但尚未能让它工作的那样:

var subject = new Subject<BesiegedMessage>();

var messagePublisher = m_ServerClient.MessageQueue
      .GetConsumingEnumerable()
      .ToObservable(TaskPoolScheduler.Default)
      .Multicast(subject);

// All generic server messages are handled here
var genericServerMessageSubscriber = subject
      .Where(message => message is GenericServerMessage)
      .Subscribe(message =>
      {
            // do something here
      }

但是现在没有一个订阅者受到攻击,包括之前工作正常的单个用户。为了能够正确地多播到多个用户,我在这里缺少什么?

更新:使用订阅(主题)而不是多播(主题)似乎正在进行多播,这使我对.MultiCast()的用途感到非常困惑

1 个答案:

答案 0 :(得分:1)

编辑:

哈哈 - 给我正确的阅读速度 - 你要问的是更简单...说,我认为下面的内容很重要,所以我要离开...所以,你的问题 - 尝试添加这个行:

var messagePublisher = m_ServerClient.MessageQueue
  .GetConsumingEnumerable()
  .ToObservable(TaskPoolScheduler.Default)
  .Multicast(subject)
  // Here: connectable observables are a PITA...
  .RefCount();

结束编辑:

嗯...如何描述Multicast ...我想让我们举例:

说你有这样的事情 - 你认为它会产生什么?

int delay = 100;
var source = Observable.Interval(TimeSpan.FromMilliseconds(delay));
var publishingFrontend = new Subject<string>();

// Here's "raw"
var rawStream = source;
using(rawStream.Subscribe(x => Console.WriteLine("{0}", x)))
{
    Thread.Sleep(delay * 3);
    using(rawStream.Subscribe(x => Console.WriteLine("Inner: {0}", x)))
    {
        Thread.Sleep(delay * 3);
    }
    Thread.Sleep(delay * 5);
}

由于您订阅了raw raw,因此新订阅者基本上从头开始:

(如果你重新开始,这不会100%匹配,因为我在Thread.Sleep采取了wussy方式,但应该关闭)

0
1
2
Inner: 0
3
Inner: 1
4
5
6
7
8
9

嗯......所以,如果我们想要“中流”,我们会使用Publish().RefCount()模式:

var singleSource = source.Publish().RefCount();
using(singleSource.Subscribe(x => Console.WriteLine("{0}", x)))
{
    Thread.Sleep(delay * 3);
    using(singleSource.Subscribe(x => Console.WriteLine("Inner: {0}", x)))
    {
        Thread.Sleep(delay * 3);
    }
    Thread.Sleep(delay * 5);
}

产生类似的东西:

0
1
2
Inner: 2
3
Inner: 3
4
Inner: 4
5
6
7
8
9

因此,假设我们没有Publish()运算符 - 我们怎么能模拟呢?

Console.WriteLine("Simulated Publish:");
// use a subject to proxy values...
var innerSubject = new Subject<long>();
// wire up the source to "write to" the subject
var innerSub = source.Subscribe(innerSubject);
var simulatedSingleSource = Observable.Create<long>(obs =>
{
    // return subscriptions to the "proxied" subject
    var publishPoint = innerSubject.Subscribe(obs);        
    return publishPoint;
});

运行这个,我们得到:

Simulated Publish:
0
1
2
Inner: 2
3
Inner: 3
4
Inner: 4
5
6
7
8
9

活泉!

但还有另一种方法......

Console.WriteLine("MulticastPublish:");
var multicastPublish = source.Multicast(new Subject<long>()).RefCount();    
using(multicastPublish.Subscribe(x => Console.WriteLine("{0}", x)))
{
    Thread.Sleep(delay * 3);
    using(multicastPublish.Subscribe(x => Console.WriteLine("Inner: {0}", x)))
    {
        Thread.Sleep(delay * 3);
    }
    Thread.Sleep(delay * 5);
}

输出:

MulticastPublish:
0
1
2
Inner: 2
3
Inner: 3
4
Inner: 4
5
6
7
8
9

编辑:

事实上,所有ConnectableObservable生成扩展都依赖于Multicast / Subject配对:

Publish() => Multicast(new Subject<T>)
Replay() => Multicast(new ReplaySubject<T>)
PublishLast() => Multicast(new AsyncSubject<T>)