C# 对于无功扩展(RX),是否可以添加一个;暂停“;指挥部?
我有一个类,它接收一个事件流,然后推出另一个事件流 所有事件都使用无功扩展(RX)。使用C# 对于无功扩展(RX),是否可以添加一个;暂停“;指挥部?,c#,.net,system.reactive,reactive-programming,rx.net,C#,.net,System.reactive,Reactive Programming,Rx.net,我有一个类,它接收一个事件流,然后推出另一个事件流 所有事件都使用无功扩展(RX)。使用.OnNext将传入的事件流从外部源推送到IObserver,并使用IObservable和订阅将传出的事件流推出。我在幕后使用Subject来管理这个 我想知道RX中有什么技术可以暂时暂停输出。这意味着传入事件将在内部队列中累积,当它们未暂停时,事件将再次流出 您可以使用可观察的模拟暂停/取消暂停 一旦pauseObservable发出“暂停”值,缓冲事件,直到pauseObservable发出“未暂停”值
.OnNext
将传入的事件流从外部源推送到IObserver
,并使用IObservable
和订阅将传出的事件流推出。我在幕后使用Subject
来管理这个
我想知道RX中有什么技术可以暂时暂停输出。这意味着传入事件将在内部队列中累积,当它们未暂停时,事件将再次流出 您可以使用可观察的模拟暂停/取消暂停
一旦pauseObservable发出“暂停”值,缓冲事件,直到pauseObservable发出“未暂停”值
下面是一个使用and的示例(来自我刚才的问题)
//输入事件,热可观察
var source=可观测的时间间隔(时间跨度从秒(1))开始)
.Select(i=>i.ToString())
.Publish().RefCount();
//模拟键盘上的暂停,实际上与此答案无关
var pauseObservable=Observable.FromEventPattern(
k=>KeyPressed+=k,k=>KeyPressed-=k)
.Select(i=>i.EventArgs.PressedKey)
.Select(i=>i==ConsoleKey.Spacebar)//空格表示暂停,其他空格表示取消暂停
.DistinctUntilChanged();
//让事件在未暂停时通过
var notPausedEvents=source.Zip(pauseObservable.MostRecent(false),(值,暂停)=>new{value,暂停})
.Where(i=>!i.paused)//未暂停
.选择(i=>i.value)
.Subscribe(Console.WriteLine);
//暂停时,缓冲直到未暂停
var pausedEvents=pauseObservable.Where(i=>i)
.Subscribe(=>
source.BufferUntil(pauseObservable.Where(i=>!i))
.Select(i=>String.Join(Environment.NewLine,i))
.Subscribe(Console.WriteLine));
改进空间:也许可以将源代码的两个订阅(pausedEvents和notPausedEvents)合并为一个。这里有一个相当简单的Rx方法来实现您想要的功能。我创建了一个名为Pausable
的扩展方法,它获取一个源可观测值和一个boolean
的第二个可观测值,用于暂停或恢复可观测值
public static IObservable<T> Pausable<T>(
this IObservable<T> source,
IObservable<bool> pauser)
{
return Observable.Create<T>(o =>
{
var paused = new SerialDisposable();
var subscription = Observable.Publish(source, ps =>
{
var values = new ReplaySubject<T>();
Func<bool, IObservable<T>> switcher = b =>
{
if (b)
{
values.Dispose();
values = new ReplaySubject<T>();
paused.Disposable = ps.Subscribe(values);
return Observable.Empty<T>();
}
else
{
return values.Concat(ps);
}
};
return pauser.StartWith(false).DistinctUntilChanged()
.Select(p => switcher(p))
.Switch();
}).Subscribe(o);
return new CompositeDisposable(subscription, paused);
});
}
public static IObservable Pausable(
这是一个可观测的来源,
IObservable暂停器)
{
返回可观察的。创建(o=>
{
var paused=new SerialDisposable();
var订阅=可观察的。发布(来源,ps=>
{
var值=新的ReplaySubject();
Func切换器=b=>
{
如果(b)
{
value.Dispose();
值=新的ReplaySubject();
暂停。一次性=ps.Subscribe(值);
return-Observable.Empty();
}
其他的
{
返回值:Concat(ps);
}
};
返回pauser.StartWith(false).DistinctUntilChanged()
.选择(p=>switcher(p))
.开关();
}).认购(o);
返回新的CompositeDisposable(订阅,暂停);
});
}
它可以这样使用:
var xs = Observable.Generate(
0,
x => x < 100,
x => x + 1,
x => x,
x => TimeSpan.FromSeconds(0.1));
var bs = new Subject<bool>();
var pxs = xs.Pausable(bs);
pxs.Subscribe(x => { /* Do stuff */ });
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
var xs=Observable.Generate(
0,
x=>x<100,
x=>x+1,
x=>x,
x=>TimeSpan.FromSeconds(0.1));
var bs=新主题();
var pxs=xs.可暂停(bs);
订阅(x=>{/*Do stuff*/});
睡眠(500);
bs.OnNext(真实);
睡眠(5000);
bs.OnNext(假);
睡眠(500);
bs.OnNext(真实);
睡眠(5000);
bs.OnNext(假);
现在,我唯一不能完全理解你所说的“传入事件流是一个IObserver
”是什么意思。流是可观察的。观察者不是溪流。听起来你好像没在这里做什么。您能补充您的问题并进一步解释吗?以下是我使用缓冲区和窗口运算符的解决方案:
public static IObservable<T> Pausable<T>(this IObservable<T> source, IObservable<bool> pauser)
{
var queue = source.Buffer(pauser.Where(toPause => !toPause),
_ => pauser.Where(toPause => toPause))
.SelectMany(l => l.ToObservable());
return source.Window(pauser.Where(toPause => toPause).StartWith(true),
_ => pauser.Where(toPause => !toPause))
.Switch()
.Merge(queue);
}
公共静态IObservable Pausable(此IObservable源,IObservable pauser)
{
var queue=source.Buffer(pauser.Where(toPause=>!toPause)),
_=>pauser.Where(toPause=>toPause))
.SelectMany(l=>l.ToObservable());
返回source.Window(pauser.Where(toPause=>toPause).StartWith(true),
_=>pauser.Where(toPause=>!toPause))
.Switch()
.合并(队列);
}
窗口在订阅时打开,并且每次从pauser stream接收到“true”时都会打开。当pauser提供“false”值时,它关闭
缓冲区执行它应该执行的操作,缓冲来自pauser的介于“false”和“true”之间的值。一旦缓冲区接收到“true”,它将立即输出一次流式传输的IList值
DotNetFiddle link:认为如果输出暂停,可能会将事件重定向到内部队列,并且当outout暂停时,它可能会刷新队列。您还没有实现自己的IObserver
,是吗?否,我所做的只是将内部主题
强制转换为IObserver
,这样方法.OnNext
就可以公开了。谢谢你的回答。我已经更新了我的问题,以使数据流更清晰。照目前的情况,这显然没有让第一个值通过,因为它通过了假分支,并希望将值浓缩到(这实际上是可观察的。我想永远不会)。我第一次将值初始化为null,并检入两个分支,从而使它成形。不确定是否还有更优雅的。进一步警告未来的自我<代码>ReplaySubject<
public static IObservable<T> Pausable<T>(this IObservable<T> source, IObservable<bool> pauser)
{
var queue = source.Buffer(pauser.Where(toPause => !toPause),
_ => pauser.Where(toPause => toPause))
.SelectMany(l => l.ToObservable());
return source.Window(pauser.Where(toPause => toPause).StartWith(true),
_ => pauser.Where(toPause => !toPause))
.Switch()
.Merge(queue);
}