根据流获取数据

时间:2015-09-23 12:44:21

标签: c# system.reactive

我们有一个通知数据更改的来源,当项目进入时我们异步获取新数据。

source.SelectMany(async n => { await FetchData()});

在等待加载数据的同时,许多通知可能会进入,但我们希望忽略除1之外的所有通知,这样我们就不会为每个通知获取数据,而是再次只进行一次。
除了1之外,我们怎样才能忽略来自源的所有通知,直到获取数据为止?

我感觉解决方案将涉及将FetchData()转换为IObservable,但我仍然不知道Rx原语允许我们组合流。

2 个答案:

答案 0 :(得分:1)

看起来像一个非常经典(但缺失)的Rx运算符的用例:ObserveLatestOn(示例实现here,但您可以在网上找到其他人)。

source.ObserveLatestOn(TimeSpan.Zero, Schedulers.NewThread).SelectMany(async n => { await FetchData()})

请注意,此实现仅在单线程调度程序上进行了测试(主要是UI,但可以使用NewThread),而不是使用Immediate / CurrentThread(可能有效)或TaskPool(可能有竞争条件)

另请注意,您在此处遇到的是Rx.Net中缺乏反应性拉backpressure(在讨论here中),RxJava对这种情况有很好的背压支持(例如{{ 3}})

答案 1 :(得分:0)

我确信有一种方法可以使用Rx,但我想到的一个简单的解决方案是使用AsyncAutoResetEvent(AutoResetEvent的异步版本)。

基本上,您创建一个异步等待的循环,以便设置AsyncAutoResetEvent,这在收到新通知时完成。自动重置确保在下次等待时,您将被异步阻止,直到收到新通知。

您可以在Stephen Cleary AsyncEx创建的优秀库中找到AsyncAutoResetEvent类作为Nuget包。

这是一个简单的程序,显示了提议的解决方案:

class Program
{
    static readonly AsyncAutoResetEvent _resetEvent = new AsyncAutoResetEvent(); 

    static void Main(string[] args)
    {
        // Start the asynchronous fetching loop...
        RunAsync();

        Task.Run(async () =>
        {
            // Simulate fast notifications 
            for (int i = 0; i < 15; i++)
            {
                OnNotification(i);
                await Task.Delay(100);
            }

            // Simulate a pause of notifications 
            await Task.Delay(2000);

            // Simulate fast notifications 
            for (int i = 0; i < 15; i++)
            {
                OnNotification(i);
                await Task.Delay(100);
            }
        });

        Console.ReadKey();
    }

    static void OnNotification(int index)
    {
        Console.WriteLine(DateTime.Now.ToLongTimeString() + " OnNotification " + index);

        // This will unlock the current or next WaitAsync on the _resetEvent
        _resetEvent.Set();
    }


    static async Task RunAsync()
    {
        // Uncomment this if you want to wait for a first notification before fetching.
        // await _resetEvent.WaitAsync();    

        while (true)
        {
            Console.WriteLine(DateTime.Now.ToLongTimeString() + " Fetching...");

            // Simulate long fetching
            await Task.Delay(1000);

            // Wait for a new notification before doing another fetch
            await _resetEvent.WaitAsync();    
        }
    }
}

这是输出:

12:04:51 PM Fetching...
12:04:51 PM OnNotification 0
12:04:52 PM OnNotification 1
12:04:52 PM OnNotification 2
12:04:52 PM OnNotification 3
12:04:52 PM OnNotification 4
12:04:52 PM OnNotification 5
12:04:52 PM OnNotification 6
12:04:52 PM OnNotification 7
12:04:52 PM OnNotification 8
12:04:52 PM OnNotification 9
12:04:52 PM Fetching...
12:04:53 PM OnNotification 10
12:04:53 PM OnNotification 11
12:04:53 PM OnNotification 12
12:04:53 PM OnNotification 13
12:04:53 PM OnNotification 14
12:04:53 PM Fetching...
12:04:55 PM OnNotification 0
12:04:55 PM Fetching...
12:04:55 PM OnNotification 1
12:04:55 PM OnNotification 2
12:04:55 PM OnNotification 3
12:04:56 PM OnNotification 4
12:04:56 PM OnNotification 5
12:04:56 PM OnNotification 6
12:04:56 PM OnNotification 7
12:04:56 PM OnNotification 8
12:04:56 PM OnNotification 9
12:04:56 PM Fetching...
12:04:56 PM OnNotification 10
12:04:56 PM OnNotification 11
12:04:56 PM OnNotification 12
12:04:57 PM OnNotification 13
12:04:57 PM OnNotification 14
12:04:57 PM Fetching...