如何使用Rx以非阻塞方式观察值?

时间:2011-06-17 10:14:22

标签: .net system.reactive

我正在尝试观察一个计时器,它的处理程序比间隔更长。 为了做到这一点,我想在某种threadPool,任务池或其他东西上安排观察。

我尝试过threadpool,taskpool和newthread,但没有一个工作。 有谁知道怎么做? 例如:

var disposable = Observable.Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(100)).ObserveOn(Scheduler.NewThread).
    Subscribe(x =>
      {
      count++;
    Thread.Sleep(TimeSpan.FromMilliseconds(1000));
  });

  Thread.Sleep(TimeSpan.FromSeconds(5));
  disposable.Dispose();
  if (count > 10 )
  {
    //hurray...
  }

3 个答案:

答案 0 :(得分:4)

你要问的是一个坏主意,因为你最终会耗尽可用资源(因为创建线程的速度>线程完成率)。相反,为什么不在前一个项目完成时安排新项目?

在您的具体示例中,您需要将IScheduler传递给Observable.Timer,而不是尝试使用ObserveOn。

答案 1 :(得分:2)

保罗是对的,他说这是一个坏主意。您在逻辑上创建排队操作可能会耗尽系统资源的情况。您甚至可以在计算机上找到它,但在客户的计算机上失败。可用内存,32/64位处理器等都可能影响代码。

但是,很容易修改代码以使其按照您的意愿进行操作。

首先,只要观察者在下一个预定事件之前完成,Timer方法就会正确调度计时器事件。如果观察者还没有完成,则计时器将等待。请记住,可观察的定时器是“冷”可观察量,因此对于每个订阅的观察者,实际上有一个新的定时器可观察。这是一对一的关系。

此行为可防止计时器无意中耗尽您的资源。

因此,当您定义代码时,每1000毫秒调用OnNext,而不是每100个调用一次。

现在,要允许代码以100毫秒的时间表运行,请执行以下操作:

Observable
    .Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(100))
    .Select(x => Scheduler.NewThread.Schedule(() =>
    {
        count++;
        Thread.Sleep(TimeSpan.FromMilliseconds(1000));
    }))
    .Subscribe(x => { });

实际上,此代码是IObservable<IDisposable>,其中每个一次性用户都是需要1000毫秒才能完成的计划操作。

在我的测试中,这很好地运行并正确递增计数。

我确实试图破坏我的资源并发现将计时器设置为每毫秒运行一次我很快得到了System.OutOfMemoryException,但我发现如果我将设置更改为每两毫秒就会运行代码。但是,当代码运行并创建大约500个新线程时,这确实占用了超过500 MB的RAM。一点也不好。

谨慎行事!

答案 2 :(得分:1)

如果你是真的,不断创造价值的速度比你消耗它们的速度快,那么正如所指出的那样,你正在走向麻烦。如果您无法降低生产速度,那么您需要了解如何更快地消耗它们。也许您想要多线程观察者使用多个核心?

如果你多线程观察者,你可能需要小心处理无序的事件。您将同时处理多个通知,并且关于首先完成哪些处理(或首先达到某个竞争条件关键状态)的所有投注均已关闭。

如果您没有拥有来处理流中的每个事件,请查看浮动的ObserveLatestOn的几个不同实现。有线程讨论它herehere

ObserveLatestOn将丢弃除观察者处理先前通知时发生的最新通知。当观察者完成对先前通知的处理时,它将接收最新通知并错过其间发生的所有通知。

这样做的好处是可以防止来自生产者的压力增加,而这种压力比消费者更快。如果消费者因负载而变慢,那么只会通过处理更多通知而变得更糟。删除不需要的通知可以允许负载后退到消费者可以跟上的点。