如何在不抛弃RX中的值的情况下减慢Observable的速度?

时间:2011-06-19 14:14:42

标签: c# .net system.reactive

我的情景: 我的计算应该每秒运行一次。在它运行之后应该等待大约200ms以便其他东西赶上来。如果一秒钟之后计算仍在运行,它应该第二次启动,但程序是否应该等到它完成并在完成后200ms开始下一次计算。

我现在这样做的方式:

_refreshFinished = new Subject<bool>();
_autoRefresher = Observable.Interval(TimeSpan.FromMilliseconds(1000))
   .Zip(_refreshFinished, (x,y) => x)
   .Subscribe(x => AutoRefresh(stuff));

这段代码的问题是,在计算结束后我看不到延迟。 Delay方法仅延迟可观察集合的第一个元素。通常这种行为是正确的,因为如果你想要缓冲所有人,你必须缓冲无限量的元素,但是由于将对Autorefesh的调用延迟200ms会将_refreshFinished的输出延迟200ms,因此不会有缓冲开销。 基本上我想要一个Observable,它会触发每个MaxTime(some_call,1000ms)然后延迟200ms甚至更好,一些动态值。在这一点上,我甚至不关心正在经历的价值观,尽管这可能会在未来发生变化。

我愿意接受任何建议

5 个答案:

答案 0 :(得分:3)

Observable.Generate()有一些重载,可让您动态调整下一个项目的创建时间。

例如

IScheduler schd = Scheduler.TaskPool;
var timeout = TimeSpan.FromSeconds(1);
var shortDelay = TimeSpan.FromMilliseconds(200);
var longerDelay = TimeSpan.FromMilliseconds(500);
Observable.Generate(schd.Now, 
                    time => true, 
                    time => schd.Now, 
                    time => new object(), // your code here
                    time => schd.Now.Subtract(time) > timeout  ? shortDelay : longerDelay ,
                    schd);

答案 1 :(得分:1)

这听起来更像是新异步框架http://msdn.microsoft.com/en-us/vstudio/gg316360

的工作

答案 2 :(得分:1)

有办法做到这一点。它不是最简单的事情,因为等待时间必须在每个值上动态计算,但它起作用并且非常通用。

当您使用此代码时,您只需插入应在YOURCODE中调用的代码,其他所有代码都会自动运行。您的代码将基本上被称为每个Max(yourCodeTime + extraDelay,ordinaryCallTime + extraDelay)。这意味着你的代码不会同时被调用两次,应用程序将总是有额外的时间来做其他事情。 如果有一些更简单/其他方式可以做到这一点,我会听到它。

double usualCallTime = 1000;
double extraDealy = 100;
var subject = new Subject<double>();
var subscription =
    sub.TimeInterval()
        .Select(x =>
            {
                var processingTime = x.Interval.TotalMilliseconds - x.Value;
                double timeToWait = 
                     Math.Max(0, usualCallTime - processingTime) + extraDelay;
                return Observable.Timer(TimeSpan.FromMilliseconds(timeToWait))
                    .Select(ignore => timeToWait);
            })
        .Switch()
        .Subscribe(x => {YOURCODE();sub.OnNext(x)});
sub.OnNext(0);

private static void YOURCODE()
{
    // do stuff here
    action.Invoke();
}

答案 3 :(得分:0)

如果我正确理解您的问题,您将拥有一个长期运行的计算功能,例如:

static String compute()
{
    int t = 300 + new Random().Next(1000);
    Console.Write("[{0}...", t);
    Thread.Sleep(t);
    Console.Write("]");
    return Guid.NewGuid().ToString();
}

并且您希望每秒至少调用一次此函数,但不会重叠调用,并且调用之间的恢复时间至少为200毫秒。以下代码适用于这种情况。

我开始使用更具功能性的方法(使用Scan()Timestamp()),更多的是Rx的风格 - 因为我正在寻找一个很好的Rx练习 - 但最后,这个非聚合方法更简单。

static void Main()
{
    TimeSpan period = TimeSpan.FromMilliseconds(1000);
    TimeSpan recovery = TimeSpan.FromMilliseconds(200);

    Observable
        .Repeat(Unit.Default)
        .Select(_ =>
        {
            var s = DateTimeOffset.Now;
            var x = compute();
            var delay = period - (DateTimeOffset.Now - s);
            if (delay < recovery)
                delay = recovery;

            Console.Write("+{0} ", (int)delay.TotalMilliseconds);

            return Observable.Return(x).Delay(delay).First();
        })
        .Subscribe(Console.WriteLine);
}

这是输出:

[1144...]+200 a7cb5d3d-34b9-4d44-95c9-3e363f518e52
[1183...]+200 359ad966-3be7-4027-8b95-1051e3fb20c2
[831...]+200 f433b4dc-d075-49fe-9c84-b790274982d9
[766...]+219 310c9521-7bee-4acc-bbca-81c706a4632a
[505...]+485 0715abfc-db9b-42e2-9ec7-880d7ff58126
[1244...]+200 30a3002a-924a-4a64-9669-095152906d85
[1284...]+200 e5b1cd79-da73-477c-bca0-0870f4b5c640
[354...]+641 a43c9df5-53e8-4b58-a0df-7561cf4b0483
[1094...]+200 8f25019c-77a0-4507-b05e-c9ab8b34bcc3
[993...]+200 840281bd-c8fd-4627-9324-372636f8dea3

[编辑:此示例使用Rx 2.0(RC)2.0.20612.0]

答案 4 :(得分:0)

假设您有一个现有的'IObservable',那么以下内容将起作用

var delay = TimeSpan.FromSeconds(1.0);
var actual = source.Scan(
    new ConcurrentQueue<object>(),
    (q, i) =>
        {
            q.Enqueue(i);
            return q;
        }).CombineLatest(
            Observable.Interval(delay),
            (q, t) =>
                {
                    object item;
                    if (q.TryDequeue(out item))
                    {
                        return item;
                    }

                    return null;
                }).Where(v => v != null);

'actual'是你的结果可观察的。但请记住,如果上面的代码已经不热,上面的代码已经将其变成了Hot observable。所以你不会被称为“OnCompleted”。