有没有办法重置Observable.Window的windowClosingSelect函数?

时间:2013-03-07 21:03:11

标签: c# system.reactive

我的目标是为“Type2”对象每5秒调用一次更新处理程序。 observable将每5秒生成多个值,但我想忽略在上次处理更新后5秒内发生的所有值。

我在这里问了这个问题: Throttle only if specific condition met

得到了很好的反馈。它让我使用Observable.Window来实现我的目标。我以为我有它工作,但事实证明,如果第一次更新在窗口关闭之前就出现(因此处理更新),然后一旦下一个窗口打开,另一个更新到达并且也被处理,它会产生不正确的输出我不希望它发生,因为它是在上次处理更新后的5秒内发生的。

以下是一些用于演示此问题的代码,稍微修改了链接中的代码:

var source = new Subject<Thing>();    
var feed = source.Publish().RefCount();

var ofType1 = feed.Where(t => t.ActivationType == "Type1");
var ofType2 = feed
    .Where(t => t.ActivationType == "Type2")
    .Window(() =>
            Observable.Timer(TimeSpan.FromSeconds(5))
            .Do(t => Console.WriteLine("\nTICK: " + DateTime.Now.ToString("hh:mm:ss:fff"))))
    .Select(x => x.Take(1))
    .Merge()
    .Do(t => Console.WriteLine("A new window opened " + DateTime.Now.ToString("hh:mm:ss:fff")));


var query = ofType1.Merge(ofType2);        
query.Subscribe(t => Console.WriteLine("UPDATE: " + t.ID + " " + DateTime.Now.ToString("hh:mm:ss:fff")));    

int msDelay = 3000;
Task task = Task.Factory
    .StartNew(() => { Thread.Sleep(msDelay); })
    .ContinueWith((Task starter) =>
        {
            while (running)
            {
                var thing = new Thing();  //Note that all Things are by default Type2
                source.OnNext(thing);
                Thread.Sleep(100);
            }
        }, TaskContinuationOptions.LongRunning);

Console.ReadLine();

因此,订阅完成,订阅完成后,Window中使用的Observable.Timer开始。用于产生值的while循环直到3000 ms延迟后才开始。

输出如下:

A new window opened 03:48:03:725
UPDATE: 1ac54fb3-f73d-4840-b4d8-95d4250ce65d 03:48:03:752

TICK: 03:48:05:714
A new window opened 03:48:05:754
UPDATE: 12d36e53-010f-4ccd-b9f8-2951b085f88c 03:48:05:754

TICK: 03:48:10:730
A new window opened 03:48:10:755
UPDATE: 25d84e72-94f9-4f50-83f4-14c1004c10fa 03:48:10:755

TICK: 03:48:15:738
A new window opened 03:48:15:755
UPDATE: 5f32b7d5-196f-445c-bf25-5c362b2fd6f0 03:48:15:755

TICK: 03:48:20:747
A new window opened 03:48:20:756
UPDATE: e3a3a30d-8031-41b5-b115-499dbe91aaf7 03:48:20:756

TICK: 03:48:25:755
A new window opened 03:48:25:756
UPDATE: 239fb25b-5135-463b-bf7e-5728ffa07f5c 03:48:25:756

正如您所看到的,第一个Type2更新在窗口打开时进入,因此会被处理。然后,2秒钟后,Window的计时器会打开,并打开一个新窗口。它立即处理下一个Type2更新,我不希望它做。之后它看起来正常工作(按照Window声明中的定义,每5秒更新一次)。

我是否可以使用某种方法或其他方法来确保每5秒(或我选择的任何时间范围)只有一次更新?

1 个答案:

答案 0 :(得分:1)

我想我有一个解决方案,但首先我可以提出一些建议。 我认为问题中存在很多噪音,这使得很难找到真正的问题。

实际上你会问“我怎样才能获得一个值,然后静音至少5秒”。 Type1代码令人分心。 序列的产生也是一种分心。

因此,让我们清理示例代码,看看我们是否可以看到树木:

首先,我不认为它的类型是完全相关的。 在您的示例中,我们从不推送Type1,因此我们只使用整数。 它可能会更容易。

接下来,我们可以通过使用Observable.Timer而不是big loop + task + thread.sleep来清理创建。

现在我们有一个简单的起点:

var source = Observable.Timer(TimeSpan.FromSeconds(3),TimeSpan.FromMilliseconds(300), Scheduler.TaskPool);   
var feed = source.Publish().RefCount();

所以我们的第一个问题是对您正在使用的Window重载的误解。 我认为你的期望是它会在推送第一个值时打开一个窗口。 事实并非如此。 定时器(即Observable.Timer(TimeSpan.FromSeconds(5)))在每次打开窗口时订阅,这最初是在订阅发生时,然后在窗口本身关闭时再次订阅。 因此,计时器立即启动,您将只获得第一个窗口的2秒值。

接下来,我会画出我的问题空间。我最喜欢的方法是用大理石图表。 他们在ASCII中不能很好地翻译,但我们可以尝试任何方式。

鉴于此输入序列:

//Seconds             1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000
//Tenths    01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
//
//source  : ------------------------------0--1--2--3--4--5--6--7--8--9--0--1--2--3--4--5--6--7--8--9--0--1--2--3--4--5--6--7--8--9--0

这应该代表在3.0秒产生的值'0'。随后是3.3秒的'1'等。

现在问题空间有点清晰,我们可以在我们认为应该打开的窗口,应该关闭的位置以及下一个窗口应该打开的位置绘制。

让我们看看我们想要的东西。

这里我们添加一个Window1(W1),它在第一个值被推送时打开('1'@ 3.0s)。它在5秒后关闭。在这个窗口期间,我们希望在第一个值为1之后保持沉默。

窗口2(W2)应该在产生下一个值后打开,而不是在最后一个窗口关闭后立即打开(我认为?!)。 在这里,我们看到当在时间8.4秒推送值“17”时,这是打开的。

//Seconds             1111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000
//Tenths    01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890

//source  : ------------------------------0--1--2--3--4--5--6--7--8--9--1--1--1--1--1--1--1--1--1--1--2--2--2--2--2--2--2--2--2--2--3--3--3--3--3--3--3--3--3--3--4--4--4--4-
                                                                        0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5  6  7  8  9  0  1  2  3
//
//W1      :                               0-------------------------------------------------|
//W2      :                                                                                  (17)----------------------------------------------|
//W3      :                                                                                                                                     (34)------------------------>

//expected: ------------------------------0--------------------------------------------------1--------------------------------------------------3---------------------------
                                                                                             7                                                  4

现在我们现在正在寻找什么值,我们可以构建一个查询。

我想出了这个。 我假设Feed实际上是Hot序列。 使用这个假设,我构造了一个重​​复的结构,从饲料中取1个值,并将5秒的静音连接到序列。 然后我添加Repeat运算符,只有在5秒的静音失效后才会重新订阅Feed。

public static IObservable<T> Silencer<T>(this IObservable<T> source, TimeSpan minSilencePeriod)
{
    return source.Take(1)
                 .Concat(Observable.Empty<T>().Delay(minSilencePeriod))
                 .Repeat();
}

这确实产生了预期的值0,17,51等。

现在将此应用于原始问题的代码(清理一些东西)

void Main()
{
    var source = Observable.Timer(TimeSpan.FromSeconds(3),TimeSpan.FromMilliseconds(300), Scheduler.TaskPool).Select(_=>new Thing());   
    var feed = source.Publish().RefCount();

    var ofType1 = feed.Where(t => t.ActivationType == "Type1");
    var ofType2 = feed
            .Where(t => t.ActivationType == "Type2")
            .Silencer(TimeSpan.FromSeconds(5));


    var query = ofType1.Merge(ofType2);        
    var subscription = query.Subscribe(t => Console.WriteLine("UPDATE: " + t.ID + " " + DateTime.Now.ToString("hh:mm:ss:fff")));    


    Console.ReadLine();
    subscription.Dispose();
}

我们看到输出的值至少相隔5秒

UPDATE: 3f0fc6f3-8a5a-476f-9661-b7330ab77877 09:14:04:725
UPDATE: fc8f0025-7a79-4329-8164-b8b421ad5865 09:14:09:817
UPDATE: ad739a71-885e-4d5b-a352-2302df0a4d87 09:14:14:925