C# 缓冲观测值排序
我有一个令牌流,生成速度非常快,而处理器相对较慢。令牌有三个子类型,我更希望它们按优先级处理。因此,我希望令牌在生成并等待处理后被缓冲,并按优先级对缓冲区进行排序 以下是我的课程:C# 缓冲观测值排序,c#,system.reactive,tpl-dataflow,C#,System.reactive,Tpl Dataflow,我有一个令牌流,生成速度非常快,而处理器相对较慢。令牌有三个子类型,我更希望它们按优先级处理。因此,我希望令牌在生成并等待处理后被缓冲,并按优先级对缓冲区进行排序 以下是我的课程: public enum Priority { High = 3, Medium = 2, Low = 1 } public class Base : IComparable<Base> { public int Id { get; set; } pub
public enum Priority
{
High = 3,
Medium = 2,
Low = 1
}
public class Base : IComparable<Base>
{
public int Id { get; set; }
public int CompareTo(Base other)
{
return Id.CompareTo(other.Id);
}
}
public class Foo : Base { }
public class Bar : Base { }
public class Baz : Base { }
public class Token : IComparable<Token>
{
private readonly string _toString;
public Foo Foo { get; }
public Bar Bar { get; }
public Baz Baz { get; }
public Priority Priority =>
Baz == null
? Bar == null
? Priority.High
: Priority.Medium
: Priority.Low;
public int CompareTo(Token other)
{
if (Priority > other.Priority)
{
return -1;
}
if (Priority < other.Priority)
{
return 1;
}
switch (Priority)
{
case Priority.High:
return Foo.CompareTo(other.Foo);
case Priority.Medium:
return Bar.CompareTo(other.Bar);
case Priority.Low:
return Baz.CompareTo(other.Baz);
default:
throw new ArgumentOutOfRangeException();
}
}
public override string ToString()
{
return _toString;
}
public Token(Foo foo)
{
_toString = $"{nameof(Foo)}:{foo.Id}";
Foo = foo;
}
public Token(Foo foo, Bar bar) : this(foo)
{
_toString += $":{nameof(Bar)}:{bar.Id}";
Bar = bar;
}
public Token(Foo foo, Baz baz) : this(foo)
{
_toString += $":{nameof(Baz)}:{baz.Id}";
Baz = baz;
}
}
然而,对于如何缓冲和排序令牌,我有点困惑。如果我这样做的话,代币看起来是同时生产和消费的,这表明在幕后有一些缓冲,但我无法控制它
tokens.Subscribe(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
如果我使用TPL数据流ActionBlock
,我可以看到令牌被正确地生成和处理,但我仍然不确定如何进行排序
var proc = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
tokens.Subscribe(dt => proc.Post(dt));
我添加了一个SortedSet
:
var set = new SortedSet<Token>();
var tokens = bazTokens
.Merge(barTokens)
.Merge(fooTokens)
.Do(dt => Display(dt, ConsoleColor.Red));
tokens.Subscribe(dt => set.Add(dt));
所以,现在我得到了我想要的结果,但是a)我对
而轮询不满意,b)如果我想要多个消费者,我会遇到竞争条件。所以,我仍然在寻找更好的实现,如果有人有一个 > P>您想要的容器是优先级队列,不幸的是.NET运行时没有实现(C++中的STL/CLI,但PrimyItyQueLe没有其他语言可用)。
有一些现有的非MS容器填充此角色,您需要搜索并查看结果,以选择一个满足您需要的容器。使用数据流,您可以过滤令牌,以便每个优先级在管道中沿不同的路径。通过在每个优先级类型的链接上使用谓词来过滤标记。然后取决于你如何根据优先级给予优先权
排序:
var highPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
var midPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
var lowPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
var proc = new BufferBlock<Token>();
proc.LinkTo(highPriority, dt => dt.Priority == Priority.High);
proc.LinkTo(midPriority, dt => dt.Priority == Priority.Medium);
proc.LinkTo(lowPriority, dt => dt.Priority == Priority.Low);
tokens.Subscribe(dt => proc.Post(dt));
var highPriOptions = new DataflowLinkOptions(){MaxDegreeOfParallelism = 3}
var highPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
}, highPriOptions);
var midPriOptions = new DataflowLinkOptions(){MaxDegreeOfParallelism = 2}
var midPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
}, midPriOptions);
var lowPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
var proc = new BufferBlock<Token>();
proc.LinkTo(highPriority, dt => dt.Priority == Priority.High);
proc.LinkTo(midPriority, dt => dt.Priority == Priority.Medium);
proc.LinkTo(lowPriority, dt => dt.Priority == Priority.Low);
tokens.Subscribe(dt => proc.Post(dt));
这些样本绝对不完整,但至少应该给你一个想法。好的,所以我使用了一个普通的锁来访问分类数据集
,然后增加了消费者的数量,它似乎工作得很好,因此,尽管我还不能提出完整的RX或拆分RX/TPL数据流解决方案,但现在这正是我想要的,所以我将在原始问题中显示除了更新之外所做的更改,并将其保留在那里
var set = new SortedSet<Token>();
var locker = new object();
var tokens = bazTokens
.Merge(barTokens)
.Merge(fooTokens)
.Do(dt => Display(dt, ConsoleColor.Red));
tokens.Subscribe(dt =>
{
lock (locker)
{
set.Add(dt);
}
});
for (var i = 0; i < Environment.ProcessorCount; i++)
{
Task.Run(() =>
{
while (!source.IsCancellationRequested)
{
Token dt;
lock (locker)
{
dt = set.FirstOrDefault();
}
if (dt == null)
{
continue;
}
bool removed;
lock (locker)
{
removed = set.Remove(dt);
}
if (removed)
{
Display(dt, ConsoleColor.Green, 750);
}
}
}, source.Token);
}
var set=new SortedSet();
var locker=新对象();
var tokens=bazTokens
.合并(巴托克斯)
.合并(fooTokens)
.Do(dt=>显示(dt,控制台颜色.Red));
tokens.Subscribe(dt=>
{
锁(储物柜)
{
加(dt);
}
});
对于(变量i=0;i
{
而(!source.IsCancellationRequested)
{
令牌dt;
锁(储物柜)
{
dt=set.FirstOrDefault();
}
如果(dt==null)
{
继续;
}
去除bool;
锁(储物柜)
{
移除=设置移除(dt);
}
如果(已删除)
{
显示器(dt,控制台颜色,绿色,750);
}
}
},source.Token);
}
感谢发布解决方案的人员,我感谢您花费的时间。我认为这里的难题是,您似乎真正想要的是基于快速、热门、推送源的拉动模式的结果。您似乎想要的是迄今为止收到的“最高”优先级,但问题是“收到的是什么?”如果您有多个订阅者,以不同的速度运行,他们可以各自对“最高”是什么有自己的看法
因此,我的看法是,您希望将源合并到一种反应式的、按优先级排序(排序)的队列中,在观察者准备就绪时从中提取结果
我通过使用返回缓冲区的信号来实现这一点,说“我的一个观察者现在准备好查看优先列表的状态”。这是通过使用接收可观察到的关闭信号的缓冲过载来实现的。该缓冲区包含接收到的元素的新列表,我刚刚将其合并到最后一个列表中,即sans'highest'
出于本问题的目的,该代码只是演示拼凑的代码-可能存在错误:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace RxTests
{
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.TestPrioritisedBuffer();
Console.ReadKey();
}
void TestPrioritisedBuffer()
{
var source1 = Observable.Interval(TimeSpan.FromSeconds(1)).Do((source) => Console.WriteLine("Source1:"+source));
var source2 = Observable.Interval(TimeSpan.FromSeconds(5)).Scan((x,y)=>(x+100)).Do((source) => Console.WriteLine("Source2:" + source)); ;
BehaviorSubject<bool> closingSelector = new BehaviorSubject<bool>(true);
var m = Observable.Merge(source1, source2).
Buffer(closingSelector).
Select(s => new { list =s.ToList(), max=(long)0 }).
Scan((x, y) =>
{
var list = x.list.Union(y.list).OrderBy(k=>k);
var max = list.LastOrDefault();
var res = new
{
list = list.Take(list.Count()-1).ToList(),
max= max
};
return res;
}
).
Do((sorted) => Console.WriteLine("Sorted max:" + sorted.max + ". Priority queue length:" + sorted.list.Count)).
ObserveOn(Scheduler.Default); //observe on other thread
m.Subscribe((v)=> { Console.WriteLine("Observed: "+v.max); Thread.Sleep(3000); closingSelector.OnNext(true); }) ;
}
}
}
使用系统;
使用System.Collections.Concurrent;
使用System.Collections.Generic;
使用System.Linq;
使用System.Reactive.Concurrency;
使用System.Reactive.Linq;
使用系统、反应、主题;
使用系统文本;
使用系统线程;
使用System.Threading.Tasks;
名称空间RxTests
{
班级计划
{
静态void Main(字符串[]参数)
{
var p=新程序();
p、 testprioritizedbuffer();
Console.ReadKey();
}
void testprioritizedbuffer()
{
var source1=Observable.Interval(TimeSpan.FromSeconds(1)).Do((source)=>Console.WriteLine(“source1:+source”);
var source2=可观察的.Interval(TimeSpan.FromSeconds(5)).Scan((x,y)=>(x+100)).Do((source)=>Console.WriteLine(“source2:+source”);
BehaviorSubject closingSelector=新的BehaviorSubject(true);
var m=可观察的.Merge(source1,source2)。
缓冲区(关闭选择器)。
选择(s=>new{list=s.ToList(),max=(long)0})。
扫描((x,y)=>
{
var list=x.list.Union(y.list).OrderBy(k=>k);
var max=list.LastOrDefault();
var res=新
{
list=list.Take(list.Count()-1).ToList(),
最大值=最大值
};
返回res;
}
).
Do((排序)=>Console.WriteLine(“排序最大值:+sorted.max+”。优先级队列长度:+sorted.list.Count))。
ObserveOn(Scheduler.Default);//在其他线程上观察
m、 Subscribe((v)=>{Console.WriteLine(“已观察:+v.max”);Thread.Sleep(3000);closingSelec
var highPriOptions = new DataflowLinkOptions(){MaxDegreeOfParallelism = 3}
var highPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
}, highPriOptions);
var midPriOptions = new DataflowLinkOptions(){MaxDegreeOfParallelism = 2}
var midPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
}, midPriOptions);
var lowPriority = new ActionBlock<Token>(dt =>
{
Thread.Sleep(TimeSpan.FromMilliseconds(250));
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"{DateTime.Now:mm:ss.fff}:{dt}");
});
var proc = new BufferBlock<Token>();
proc.LinkTo(highPriority, dt => dt.Priority == Priority.High);
proc.LinkTo(midPriority, dt => dt.Priority == Priority.Medium);
proc.LinkTo(lowPriority, dt => dt.Priority == Priority.Low);
tokens.Subscribe(dt => proc.Post(dt));
var set = new SortedSet<Token>();
var locker = new object();
var tokens = bazTokens
.Merge(barTokens)
.Merge(fooTokens)
.Do(dt => Display(dt, ConsoleColor.Red));
tokens.Subscribe(dt =>
{
lock (locker)
{
set.Add(dt);
}
});
for (var i = 0; i < Environment.ProcessorCount; i++)
{
Task.Run(() =>
{
while (!source.IsCancellationRequested)
{
Token dt;
lock (locker)
{
dt = set.FirstOrDefault();
}
if (dt == null)
{
continue;
}
bool removed;
lock (locker)
{
removed = set.Remove(dt);
}
if (removed)
{
Display(dt, ConsoleColor.Green, 750);
}
}
}, source.Token);
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace RxTests
{
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.TestPrioritisedBuffer();
Console.ReadKey();
}
void TestPrioritisedBuffer()
{
var source1 = Observable.Interval(TimeSpan.FromSeconds(1)).Do((source) => Console.WriteLine("Source1:"+source));
var source2 = Observable.Interval(TimeSpan.FromSeconds(5)).Scan((x,y)=>(x+100)).Do((source) => Console.WriteLine("Source2:" + source)); ;
BehaviorSubject<bool> closingSelector = new BehaviorSubject<bool>(true);
var m = Observable.Merge(source1, source2).
Buffer(closingSelector).
Select(s => new { list =s.ToList(), max=(long)0 }).
Scan((x, y) =>
{
var list = x.list.Union(y.list).OrderBy(k=>k);
var max = list.LastOrDefault();
var res = new
{
list = list.Take(list.Count()-1).ToList(),
max= max
};
return res;
}
).
Do((sorted) => Console.WriteLine("Sorted max:" + sorted.max + ". Priority queue length:" + sorted.list.Count)).
ObserveOn(Scheduler.Default); //observe on other thread
m.Subscribe((v)=> { Console.WriteLine("Observed: "+v.max); Thread.Sleep(3000); closingSelector.OnNext(true); }) ;
}
}
}