我正在尝试获取可观察的20个最新值,并将其作为属性公开而不会发生阻塞。目前,我的代码如下:
class Foo
{
private IObservable<int> observable;
public Foo(IObservable<int> bar)
{
this.observable = bar;
}
public IEnumerable<int> MostRecentBars
{
get
{
return this.observable.TakeLast(20).ToEnumerable();
}
}
}
但是,当调用MostRecentBars getter时,这是阻塞的,可能是因为ToEnumerable在至少有20个观察值之前不会返回。
是否有一种内置方法可以在不阻塞的情况下最多暴露20个最近观察到的值?如果观察值少于20,那么它应该只返回所有值。
答案 0 :(得分:2)
我会给你两个选择。一个使用Rx Scan
运算符,但我认为这使得它阅读起来有点复杂。另一个使用带锁定的标准Queue
。你可以选择。
(1)
class Foo
{
private int[] bars = new int[] { };
public Foo(IObservable<int> bar)
{
bar
.Scan<int, int[]>(
new int[] { },
(ns, n) =>
ns
.Concat(new [] { n, })
.TakeLast(20)
.ToArray())
.Subscribe(ns => bars = ns);
}
public IEnumerable<int> MostRecentBars
{
get
{
return bars;
}
}
}
(2)
class Foo
{
private Queue<int> queue = new Queue<int>();
public Foo(IObservable<int> bar)
{
bar.Subscribe(n =>
{
lock (queue)
{
queue.Enqueue(n);
if (queue.Count > 20)
{
queue.Dequeue();
}
}
});
}
public IEnumerable<int> MostRecentBars
{
get
{
lock (queue)
{
return queue.ToArray();
}
}
}
}
我希望这些帮助。
答案 1 :(得分:1)
我有一些扩展,我倾向于附加到我使用反应式扩展构建的任何项目,其中一个是滑动窗口:
public static IObservable<IEnumerable<T>> SlidingWindow<T>(this IObservable<T> o, int length)
{
Queue<T> window = new Queue<T>();
return o.Scan<T, IEnumerable<T>>(new T[0], (a, b) =>
{
window.Enqueue(b);
if (window.Count > length)
window.Dequeue();
return window.ToArray();
});
}
这将返回最近N个项目的数组(如果还没有N项,则返回更少)。
对于您的情况,您应该能够:
class Foo
{
private IObservable<int> observable;
private int[] latestWindow = new int[0];
IDisposable slidingWindowSubscription;
public Foo(IObservable<int> bar)
{
this.observable = bar;
slidingWindowSubscription = this.observable.SlidingWindow(20).Subscribe(a =>
{
latestWindow = a;
});
}
public IEnumerable<int> MostRecentBars
{
get
{
return latestWindow;
}
}
}
答案 2 :(得分:0)
我想不出符合您要求的内置Rx运算符。你可以这样实现它:
class Foo
{
private IObservable<int> observable;
private Queue<int> buffer = new Queue<int>();
public Foo(IObservable<int> bar)
{
this.observable = bar;
this.observable
.Subscribe(item =>
{
lock (buffer)
{
if (buffer.Count == 20) buffer.Dequeue();
buffer.Enqueue(item);
}
});
}
public IEnumerable<int> MostRecentBars
{
get
{
lock (buffer)
{
return buffer.ToList(); // Create a copy.
}
}
}
}
答案 3 :(得分:0)
虽然你已经得到了答案,但我正在考虑使用带有缓冲区的Replay Subject来解决这个问题,并想出了类似的内容:
class Foo
{
private ReplaySubject<int> replay = new ReplaySubject<int>(20);
public Foo(IObservable<int> bar)
{
bar.Subscribe(replay);
}
public IEnumerable<int> MostRecentBars
{
get
{
var result = new List<int>();
replay.Subscribe(result.Add); //Replay fill in the list with buffered items on same thread
return result;
}
}
}
如果这符合您的问题,请告诉我。
答案 4 :(得分:-1)
严重的是,这是否取决于您愿意等待多长时间?当您手头有19个项目时,您如何知道可观察的对象已经完成?一秒钟后,您确定完成了吗?十年后?您确定完成了吗?你怎么知道的?它是可观察的,因此您必须一直观察它,直到您的运算符或您应用的任何单子变换对传入流进行一些有用的变换为止。
我认为带有TimeSpan重载的(可能是新添加的)Window或Buffer可以工作。尤其是Window,它会释放一个Observable of Observable,因此,一旦创建了外部Observable,您实际上可以侦听20个项目中的第一个,然后需要仔细侦听OnCompleted,否则您将失去Window运算符的全部知识,但是你明白了。