簡體   English   中英

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

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

我的情況:我有一個計算應該每秒運行一次。 它運行后,應等待大約200毫秒,以便其他內容趕上來。 如果計算在一秒鍾后仍在運行,則應再次啟動計算,但程序應等待直到完成,並在完成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,因此也就不會有緩沖開銷。 基本上,我希望每一個MaxTime(some_call,1000ms)都會觸發一個Observable,然后延遲200ms甚至更好,延遲一些動態值。 在這一點上,我什至都不在乎正在運行的值,盡管將來可能會改變。

我願意接受任何建議

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

這聽起來更像是新異步框架的工作http://msdn.microsoft.com/en-us/vstudio/gg316360

有一種方法可以做到。 這不是有史以來最容易的事情,因為必須根據每個值動態計算等待時間,但是它可以正常工作並且非常通用。

使用此代碼時,您只需插入應該在YOURCODE中調用的代碼,其他所有內容就會自動運行。 基本上每個Max(yourCodeTime + extraDelay,usualCallTime + extraDelay)都會調用您的代碼。 這意味着yourCode不會同時被調用兩次,並且該應用程序總是有多余的時間來做其他事情。 如果有一些更簡單/其他的方式可以做到這一點,我會想聽聽的。

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

如果我正確地理解了您的問題,則您可以使用以下長期運行的計算功能:

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

而且您希望每秒至少調用一次此函數,但不要重疊調用,並且兩次調用之間至少要有200ms的恢復時間。 下面的代碼適用於這種情況。

我從一種更具功能性的方法(使用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]

假設您有一個現有的“ 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);

“實際”是您可以觀察到的結果。 但是請記住,如果上面的代碼還不是很熱,那么上面的代碼就將其變為可觀察到的。 這樣就不會調用“ OnCompleted”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM