[英]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.