[英]How to request an item from IObservable?
最初的帖子包含一个问题,我设法解决了,并引入了许多共享状态可变的问题。 现在,我想知道是否可以通过纯粹的功能方式完成此操作。
可以按一定顺序处理请求。
对于每个订单i
都有一个有效性E(i)
处理要求应符合三个条件
获取第一个请求和处理它之间应该没有延迟
处理某些请求和处理下一个请求之间应该没有延迟
当处理请求有多个订单时,应选择效果最好的一个
具体示例:
对于无限的整数列表,请打印它们,以便质数通常早于质数
订购的有效性与排队中素数但打印非素数的次数相反
我在C#
第一个解决方案(显然不是针对质数)使用了一些类,这些类具有由并发优先级队列表示的共享可变状态。 这很丑陋,因为我必须手动为事件订阅类并取消订阅它们,在其他使用者处理该队列之前,请确保一个中间使用者没有耗尽队列,等等。
为了重构它,我选择了Reactive Extensions库,该库似乎解决了状态问题。 我了解在以下情况下无法使用它:
source
函数不接受任何内容并返回IObservable<Request>
process
函数接受IObservable<Request>
并且不返回任何内容
我必须编写一个reorder
功能,该功能可以按照从source
到process
的方式对请求进行重新排序。
内部reorder
具有一个ConcurrentPriorityQueue
订单。 它应该处理两种情况:
当process
忙于处理reorder
可以找到更好的排序并更新队列
当process
请求新订单reorder
返回队列中的第一个元素
问题是,如果reorder
返回IObservable<Request>
,则不知道是否从中请求了项目,还是没有。
如果reorder
在收到后立即调用了OnNext
,则它没有重新排序任何东西并违反了条件3。
如果确保已找到最佳排序,则它将违反条件1&2,因为process
可能变得空闲。
如果reorder
返回ISubject<Request>
,则它向消费者提供一个调用OnError
和OnCompleted
的选项。
如果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();
}
}
}
总结(从概念上):
source
),以及可以处理这些请求的单个处理器(功能process
)。 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.