根据CPU使用情况调整RX任务

时间:2014-08-07 09:36:05

标签: system.reactive

我有一个长期运行的任务(使用Kinect One从深度图像创建纹理),它是使用Reactive Extensions实现的。它的要点如下:

kinectWrapper.DepthFrames
    .ObserveOn(new EventLoopScheduler())
    .Select(f => do some CPU intensive data manipulation to create the color texture I want)
    .Subscribe(colorFrame => fill texture on GPU)

问题是选择和订阅在系统上都很重,并且不会全速运行。我已经设法在我的开发PC上以.Sample(TimeSpan.FromMilliseconds(100))以可接受的速度运行它,但我宁愿让它根据CPU使用率降低帧速率。

我认为有两种可能性:

  1. 创建辅助IObservable作为Sample的输入,动态限制主事件循环。
  2. 编写我自己的IScheduler,它会在任务被淹没时丢弃计划任务。

1 个答案:

答案 0 :(得分:2)

可以通过修改此处的扩展方法的行为来实现解决方案:http://rxx.codeplex.com/workitem/20724

下面是一个例子。在这种情况下,我修改了行为,以便扩展方法通过丢弃最旧的通知来限制排队通知的数量,直到队列大小可以接受为止。

为了满足您的要求,您可以对其进行修改,以便根据您可以使用System.Diagnostics.PerformanceCounter类读取的CPU指标丢弃某些通知。

但是,您也可以尝试从这些具体细节中抽象出来,也许您可​​以使用下面的扩展方法和使用低优先级线程的调度程序。

这意味着当CPU忙时,通知更有可能被丢弃。

kinectWrapper.DepthFrames.ThrottledObserveOn(
    new EventLoopScheduler(start => new Thread(start) {Priority = ThreadPriority.Lowest, IsBackground = true}),
    5).Select(...

public static IObservable<TSource> ThrottledObserveOn<TSource>(
    this IObservable<TSource> source,
    IScheduler scheduler,
    int maximumQueuedNotifications)
{
    Contract.Requires(source != null);
    Contract.Requires(scheduler != null);
    Contract.Requires(maximumQueuedNotifications >= 0);

    return Observable.Create<TSource>(observer =>
    {
        var notificationsGate = new object();
        var acceptingNotification = false;
        var nextNotifications = new Queue<Notification<TSource>>();
        Notification<TSource> completionNotification = null;
        var schedulerDisposable = new MultipleAssignmentDisposable();

        var subscriptionDisposable = source.Materialize().Subscribe(notification =>
        {
            bool startAcceptingNotifications;

            lock (notificationsGate)
            {
                startAcceptingNotifications = !acceptingNotification;
                acceptingNotification = true;

                if (notification.Kind == NotificationKind.OnNext)
                {
                    nextNotifications.Enqueue(notification);
                }
                else
                {
                    completionNotification = notification;
                }
            }

            if (startAcceptingNotifications)
            {
                schedulerDisposable.Disposable = scheduler.Schedule(rescheduleAction =>
                {
                    Notification<TSource> notificationToAccept;
                    lock (notificationsGate)
                    {
                        if (nextNotifications.Any())
                        {
                            do
                            {
                                notificationToAccept = nextNotifications.Dequeue();
                            }
                            while (nextNotifications.Count > maximumQueuedNotifications);
                        }
                        else
                        {
                            notificationToAccept = completionNotification;
                            completionNotification = null;
                        }
                    }

                    notificationToAccept.Accept(observer);

                    bool continueAcceptingNotification;

                    lock (notificationsGate)
                    {
                        continueAcceptingNotification = acceptingNotification = nextNotifications.Any() || completionNotification != null;
                    }

                    if (continueAcceptingNotification)
                    {
                        rescheduleAction();
                    }
                });
            }
        });
        return new CompositeDisposable(subscriptionDisposable, schedulerDisposable);
    });
}