我的情景: 我的计算应该每秒运行一次。在它运行之后应该等待大约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甚至更好,一些动态值。在这一点上,我甚至不关心正在经历的价值观,尽管这可能会在未来发生变化。
我愿意接受任何建议
答案 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”。