简体   繁体   English

如何在不丢掉RX中的值的情况下放慢Observable的速度?

[英]How can i slow down an Observable without throwing away values in RX?

My scenario: I have a computation that should be run about once a second. 我的情况:我有一个计算应该每秒运行一次。 After it is run there should be a wait of about 200ms for other stuff to catch up. 它运行后,应等待大约200毫秒,以便其他内容赶上来。 If the compuation is still running after a second it should be started a second time, but should the program should wait until it is finished and start the next computation 200ms after finishing. 如果计算在一秒钟后仍在运行,则应再次启动计算,但程序应等待直到完成,并在完成200ms后开始下一次计算。

The way I am doing it now: 我现在的做法是:

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

The problem with this code is, that i see no way to put in a delay after a computation finished. 这段代码的问题是,我看不到在计算完成后要延迟的方法。 The Delay method only delays the first element of the observable collection. Delay方法仅延迟可观察集合的第一个元素。 Usually this behaviour is the right once, since you would have to buffer an endless amount of elements if you wanted to buffer everyone, but since delaying the call to Autorefesh by 200ms delays the output of _refreshFinished by 200ms as well there would be no buffer overhead. 通常,这种行为是正确的一次,因为如果要缓冲所有人,则必须缓冲无数个元素,但是由于将对Autorefesh的调用延迟了200ms,因此_refreshFinished的输出也会延迟200ms,因此也就不会有缓冲开销。 Basicly I want an Observable that fires every every MaxTime(some_call,1000ms) then gets delayed by 200ms or even better, some dynamic value. 基本上,我希望每一个MaxTime(some_call,1000ms)都会触发一个Observable,然后延迟200ms甚至更好,延迟一些动态值。 At this point i dont even really care about the values that are running through this, although that might change in the future. 在这一点上,我什至都不在乎正在运行的值,尽管将来可能会改变。

I´m open to any suggestions 我愿意接受任何建议

Observable.Generate() has a number of overloads which will let you dynamically adjust the time in which the next item is created. Observable.Generate()具有许多重载,这些重载将使您可以动态调整下一个项目的创建时间。

For instance 例如

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);

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

There is a way to do it. 有一种方法可以做到。 Its not the easiest thing ever, since the wait time has to be dynamicly calculated on each value but it works and is pretty generic. 这不是有史以来最容易的事情,因为必须根据每个值动态计算等待时间,但是它可以正常工作并且非常通用。

When you use thise code you can just insert the code that should be called in YOURCODE and everything else works automaticly. 使用此代码时,您只需插入应该在YOURCODE中调用的代码,其他所有内容就会自动运行。 You code will be basicly be called every Max(yourCodeTime+extraDelay,usualCallTime+extraDelay). 基本上每个Max(yourCodeTime + extraDelay,usualCallTime + extraDelay)都会调用您的代码。 This means yourCode wont be called twice at the same time and the app will always have extraDelay of time to do other stuff. 这意味着yourCode不会同时被调用两次,并且该应用程序总是有多余的时间来做其他事情。 If there is some easier/other way to do this i would ove to hear it. 如果有一些更简单/其他的方式可以做到这一点,我会想听听的。

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();
}

If I understand your problem correctly, you have a long-running compute function such as this: 如果我正确地理解了您的问题,则您可以使用以下长期运行的计算功能:

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

And you want to call this function at least once per second but without overlapping calls, and with a minimum 200ms recovery time between calls. 而且您希望每秒至少调用一次此函数,但不要重叠调用,并且两次调用之间至少要有200ms的恢复时间。 The code below works for this situation. 下面的代码适用于这种情况。

I started with a more functional approach (using Scan() and Timestamp() ), more in the style of Rx--because I was looking for a good Rx exercise--but in the end, this non-aggregating approach was just simpler. 我从一种更具功能性的方法(使用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);
}

Here's the output: 这是输出:

[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

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

Suppose you have an existing 'IObservable' , then the following will work 假设您有一个现有的“ 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' is your resultant observable. “实际”是您可以观察到的结果。 But keep in mind that the above code has turned that into a Hot observable if it wasn't hot already. 但是请记住,如果上面的代码还不是很热,那么上面的代码就将其变为可观察到的。 So you won't get 'OnCompleted' called. 这样就不会调用“ OnCompleted”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 节流Rx.Observable而不跳过值 - Throttle Rx.Observable without skipping values 学习Rx:如何将可观察的字符序列解析为可观察的字符串序列? - Learning Rx: How can I parse an observable sequence of characters into an observable sequence of strings? 接收:创建无主题的可观察对象 - Rx: Create observable without subject 在Rx.Net中,如何在反馈耗尽之前实现可观察的反馈循环? - In Rx.Net how can I implement an observable feedback loop until the feedback is exhausted? 退订后,我如何等待一切按Rx可观察的顺序进行? - How can I await that everything is done in a Rx observable sequence after unsubscribe? 如何从多个DBContext创建可观察的热RX - How do I create a hot RX observable from multiple DBContexts 如何将实体推送到Rx Observable? - How do I push an entity onto an Rx Observable? 如何创建一个 Rx observable 来获取即时值然后采样? - How do I create an Rx observable that gets an immediate value and then samples? 如何在Rx中创建一个可观察的“通道”? - How to create an Observable “channel” in Rx? 如何将使用Rx Observable.FromEventPattern编写的用于事件处理程序的函数转换为纯.net事件处理程序(不带Rx) - How to convert a function written using Rx Observable.FromEventPattern for event handlers to a pure .net event handler(without Rx)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM