原帖包含一个问题,我设法解决了,引入了很多共享可变状态的问题。现在,我想知道,是否可以以纯粹的功能方式完成。
请求可以按特定顺序处理。
对于每个订单i
,都有效E(i)
处理请求应遵循三个条件
获取第一个请求和处理第一个请求之间应该没有延迟
处理某个请求和处理下一个请求之间应该没有延迟
当有多个处理请求订单时,应选择效率最高的订单
具体例子:
对于无限的整数列表,打印它们,因此,素数通常早于素数
排序的有效性与我们在队列中有素数的次数相反,但是打印非素数
我在C#
中的第一个解决方案(显然不是针对素数)使用了一些具有由并发优先级队列表示的共享可变状态的类。这很丑陋,因为我不得不手动订阅类到事件并取消订阅它们,检查一个中间消费者在其他消费者处理它之前没有用尽队列等等。
为了重构它,我选择了Reactive Extensions库,它似乎解决了state的问题。我明白在以下情况下我无法使用它:
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>
当只有一个订阅者时,它可以正常工作,但是如果使用的请求数量多于源数据生成的数量,那么它们将永远等待。
这个问题可以通过引入缓存来解决,但是快速消耗队列的消费者会对所有其他消费者产生副作用,因为他会在一些等待之后以较低的有效顺序冻结队列。 / p>
所以,我会在最初的问题上发布解决方案,但它并不是一个非常有价值的答案,因为它会引入很多的问题。
这说明了为什么功能性反应式编程和副作用不能很好地混合。另一方面,我现在似乎有一个无法用纯粹的功能方式解决的实际问题的例子。或者不是吗?如果Order
函数接受optimizationLevel
作为参数,则它将是纯粹的。我们能否以某种方式隐含地将时间转换为optimizationLevel
以使其变得纯净?
我非常希望看到这样的解决方案。使用C#
或任何其他语言。
有问题的解决方案。使用来自this repo。
的ConcurrentPriorityQueueusing 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();
}
}
}
答案 0 :(得分:0)
总结(概念上):
source
),以及可以处理它们的单个处理器(函数process
)。 reorder
)。这一切都正确吗?
假设它是source
可以是IObservable<Request>
类型,听起来不错。 reorder
虽然听起来确实应该返回IEnumerable<Request>
:process
想要以拉取为基础:它想要在释放后拉出最高优先级请求,并等待如果队列为空但立即启动的下一个请求。这听起来像是IEnumerable
的任务,而不是IObservable
。
public IObservable<Request> requester();
public IEnumerable<Request> reorder(IObservable<Request> requester);
public void process(IEnumerable<Request> requestEnumerable);