繁体   English   中英

如何从IObservable请求商品?

[英]How to request an item from IObservable?

最初的帖子包含一个问题,我设法解决了,并引入了许多共享状态可变的问题。 现在,我想知道是否可以通过纯粹的功能方式完成此操作。


可以按一定顺序处理请求。

对于每个订单i都有一个有效性E(i)

处理要求应符合三个条件

  1. 获取第一个请求和处理它之间应该没有延迟

  2. 处理某些请求和处理下一个请求之间应该没有延迟

  3. 当处理请求有多个订单时,应选择效果最好的一个


具体示例:

对于无限的整数列表,请打印它们,以便质数通常早于质数

订购的有效性与排队中素数但打印非素数的次数相反


我在C#第一个解决方案(显然不是针对质数)使用了一些类,这些类具有由并发优先级队列表示的共享可变状态。 这很丑陋,因为我必须手动为事件订阅类并取消订阅它们,在其他使用者处理该队列之前,请确保一个中间使用者没有耗尽队列,等等。

为了重构它,我选择了Reactive Extensions库,该库似乎解决了状态问题。 我了解在以下情况下无法使用它:


source函数不接受任何内容并返回IObservable<Request>

process函数接受IObservable<Request>并且不返回任何内容

我必须编写一个reorder功能,该功能可以按照从sourceprocess的方式对请求进行重新排序。

内部reorder具有一个ConcurrentPriorityQueue订单。 它应该处理两种情况:

  1. process忙于处理reorder可以找到更好的排序并更新队列

  2. process请求新订单reorder返回队列中的第一个元素


问题是,如果reorder返回IObservable<Request> ,则不知道是否从中请求了项目,还是没有。

如果reorder在收到后立即调用了OnNext ,则它没有重新排序任何东西并违反了条件3。

如果确保已找到最佳排序,则它将违反条件1&2,因为process可能变得空闲。

如果reorder返回ISubject<Request> ,则它向消费者提供一个调用OnErrorOnCompleted的选项。

如果reorder已返回队列,我将返回到开始的地方


问题是,冷的IObservable.Create不够懒。 订阅时,它开始耗尽所有请求的队列,但仅使用第一个请求的结果。

我想出的解决方案是返回可观察的请求,即IObservable<Func<Task<int>>>而不是IObservable<int>

当只有一个订户时,它可以工作,但是如果使用的请求数量超过了源生成的数量,则将永远等待它们。

可以通过引入缓存来解决此问题,但是快速消耗队列的使用者将对所有其他使用者产生副作用,因为与等待一段时间后相比,他将以不太有效的顺序冻结队列。

因此,我将发布原始问题的解决方案,但这并不是真正有价值的答案,因为它引入了许多问题。

这说明了为什么功能性反应式编程和副作用不能很好地混合。 另一方面,我现在似乎有一个无法通过纯功能方法解决的实际问题的示例。 是不是 如果Order函数接受optimizationLevel作为参数,它将是纯净的。 我们是否可以通过某种方式将时间隐式转换为optimizationLevel以使其完全相同?

我非常希望看到这样的解决方案。 使用C#或任何其他语言。


有问题的解决方案。 使用仓库中的ConcurrentPriorityQueue。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Reactive.Linq;
using DataStructures;
using System.Threading;

namespace LazyObservable
{
    class Program
    {
        /// <summary>
        /// Compares tuple by second element, then by first in reverse
        /// </summary>
        class PriorityComparer<TElement, TPriority> : IComparer<Tuple<TElement, TPriority>>
            where TPriority : IComparable<TPriority>
        {
            Func<TElement, TElement, int> fallbackComparer;
            public PriorityComparer(IComparer<TElement> comparer=null)
            {
                if (comparer != null)
                {
                    fallbackComparer = comparer.Compare;
                }
                else if (typeof(IComparable<TElement>).IsAssignableFrom(typeof(TElement))
                    || typeof(IComparable).IsAssignableFrom(typeof(TElement)))
                {
                    fallbackComparer = (a,b)=>-Comparer<TElement>.Default.Compare(a,b);
                }
                else
                {
                    fallbackComparer = (_1,_2) => 0;
                }
            }
            public int Compare(Tuple<TElement, TPriority> x, Tuple<TElement, TPriority> y)
            {
                if (x == null && y == null)
                {
                    return 0;
                }
                if (x == null || y == null)
                {
                    return x == null ? -1 : 1;
                }
                int res=x.Item2.CompareTo(y.Item2);
                if (res == 0)
                {
                    res = fallbackComparer(x.Item1,y.Item1);
                }
                return res;
            }
        };
        const int N = 100;
        static IObservable<int> Source()
        {
            return Observable.Interval(TimeSpan.FromMilliseconds(1))
                .Select(x => (int)x)
                .Where(x => x <= 100);
        }
        static bool IsPrime(int x)
        {
            if (x <= 1)
            {
                return false;
            }
            if (x == 2)
            {
                return true;
            }
            int limit = ((int)Math.Sqrt(x)) + 1;
            for (int i = 2; i < limit; ++i)
            {
                if (x % i == 0)
                {
                    return false;
                }
            }
            return true;
        }
        static IObservable<Func<Task<int>>> Order(IObservable<int> numbers)
        {
            ConcurrentPriorityQueue<Tuple<int, int>> queue = new ConcurrentPriorityQueue<Tuple<int, int>>(new PriorityComparer<int, int>());
            numbers.Subscribe(x =>
            {
                queue.Add(new Tuple<int, int>(x, 0));
            });
            numbers
                .ForEachAsync(x=>
                {
                    Console.WriteLine("Testing {0}", x);
                    if (IsPrime(x))
                    {
                        if (queue.Remove(new Tuple<int, int>(x, 0)))
                        {
                            Console.WriteLine("Accelerated {0}", x);
                            queue.Add(new Tuple<int, int>(x, 1));
                        }
                    }
                });
            Func<Task<int>> requestElement = async () =>
              {
                  while (queue.Count == 0)
                  {
                      await Task.Delay(30);
                  }
                  return queue.Take().Item1;
              };
            return numbers.Select(_=>requestElement);
        }
        static void Process(IObservable<Func<Task<int>>> numbers)
        {
            numbers
                .Subscribe(async x=>
                {
                    await Task.Delay(1000);
                    Console.WriteLine(await x());
                });
        }

        static void Main(string[] args)
        {
            Console.WriteLine("init");
            Process(Order(Source()));
            //Process(Source());
            Console.WriteLine("called");
            Console.ReadLine();
        }
    }
}

总结(从概念上):

  1. 您有不定期收到的请求(来自source ),以及可以处理这些请求的单个处理器(功能process )。
  2. 处理器应无停机时间。
  3. 您将隐式需要某种队列式的集合来管理请求的传入速度快于处理器处理速度的情况。
  4. 如果有多个请求排队,理想情况下,您应该通过一些有效性功能对其进行排序,但是重新排序不应成为停机的原因。 (功能reorder )。

这一切正确吗?

假设是, source可以是IObservable<Request>类型,听起来不错。 reorder尽管听起来确实应该返回一个IEnumerable<Request>process希望在pull-basis上工作:它希望在释放后拉出最高优先级的请求,如果队列为空,则等待下一个请求,但是立即开始。 这听起来像IEnumerable而不是IObservable的任务。

public IObservable<Request> requester();
public IEnumerable<Request> reorder(IObservable<Request> requester);
public void process(IEnumerable<Request> requestEnumerable);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM