C# 基于流的数据获取

C# 基于流的数据获取,c#,system.reactive,C#,System.reactive,我们有一个通知数据更改的源,当项目进入时,我们异步获取新数据 source.SelectMany(async n => { await FetchData()}); 在等待加载数据时,可能会收到许多通知,但我们希望忽略除1之外的所有通知,这样我们就不会为每个通知获取数据,而是只执行一次。 在获取数据之前,我们如何忽略来自源的所有通知(1除外) 我感觉解决方案将涉及将FetchData()转换为IObservable,但我仍然不知道Rx原语将允许我们组合流。我确信有一种方法可以用Rx实现,

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

source.SelectMany(async n => { await FetchData()});
在等待加载数据时,可能会收到许多通知,但我们希望忽略除1之外的所有通知,这样我们就不会为每个通知获取数据,而是只执行一次。
在获取数据之前,我们如何忽略来自源的所有通知(1除外)


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

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

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

您可以在Stephen Cleary创建的优秀库中找到AsyncAutoResteEvent类,它是一个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();    
        }
    }
}

看起来像是一个非常经典(但缺少)的Rx操作符的用例:
ObserveLatestOn
(示例实现,但您可以在web上找到其他实现)

请注意,此实现仅在单线程调度程序(主要是UI,但将与NewThread一起使用)上进行过测试,而不在
立即
/
当前线程
(可能有效)或
任务池
(可能存在争用条件)上进行过测试


还请注意,您在这里遇到的问题是Rx.Net中缺少反应性拉入(在讨论中),RxJava对这种情况有很好的背压支持(例如)

我们正在Rx中寻找解决方案,因为我们正在处理输入和输出上的IObservable以及其他Rx转换。
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...
source.ObserveLatestOn(TimeSpan.Zero, Schedulers.NewThread).SelectMany(async n => { await FetchData()})