C# 4.0 这是否可能使用反应式框架?
我的C#4.0应用程序中有一个对象列表。假设此列表包含100个student类对象。在反应式框架中,有没有办法一次并行执行10个对象 每个student对象运行一个方法,该方法大约需要10到15秒的时间。第一次,从列表中取出前10个学生对象,等待所有10个学生对象完成工作,然后取出下10个学生对象,依此类推,直到完成列表中的全部项目C# 4.0 这是否可能使用反应式框架?,c#-4.0,system.reactive,C# 4.0,System.reactive,我的C#4.0应用程序中有一个对象列表。假设此列表包含100个student类对象。在反应式框架中,有没有办法一次并行执行10个对象 每个student对象运行一个方法,该方法大约需要10到15秒的时间。第一次,从列表中取出前10个学生对象,等待所有10个学生对象完成工作,然后取出下10个学生对象,依此类推,直到完成列表中的全部项目 我有一个计数为100的列表 首先从列表中取出10项,并行调用每个对象的long-run方法 接收每个对象的返回值并更新UI[订阅部分] 下一轮仅在前10轮完成并释放
列表
var students = new List<Student>();
{....}
var cancel = students
.ToObservable(Scheduler.Default)
.Window(10)
.Merge(1)
.Subscribe(tenStudents =>
{
tenStudents.ObserveOn(Scheduler.Default)
.Do(x => DoSomeWork(x))
.ObserverOnDispatcher()
.Do(tenStudents => UpdateUI(tenStudents))
.Subscribe();
});
var students=newlist();
{....}
var取消=学生
.ToObservable(Scheduler.Default)
.窗口(10)
.合并(1)
.订阅(时态学生=>
{
tenStudents.ObserveOn(Scheduler.Default)
.Do(x=>DoSomeWork(x))
.ObserverOnDispatcher()
.Do(tenStudents=>UpdateUI(tenStudents))
.Subscribe();
});
此版本每次将始终有10名学生参加。当一个学生完成时,另一个学生将开始。当每个学生完成时,您可以处理其所发生的任何错误,然后将其清除(这将在下一个学生开始跑步之前发生)
这不完全是你所要求的,因为它没有在开始下一批之前完成第一批10个。这总是保持10个并行运行。如果你真的想要10个批次,我会调整代码。这对我来说听起来很像TPL的问题。您有一组已知的静态数据。您希望划分一些繁重的处理以并行运行,并且希望能够批处理负载 在您的问题中,我看不到任何异步源、动态数据源或需要响应的消费者。这就是我建议您使用第三方物流的理由 另一方面,为什么魔法数字10要并行处理?这是一项业务需求,还是一种优化性能的潜在尝试?通常,最佳做法是允许TaskPool根据内核数量和当前负载计算出最适合客户端CPU的内容。我认为,随着设备及其CPU结构的巨大变化(单核、多核、多核、低功耗/禁用核等),这一点变得越来越重要 这里有一种方法可以在LinqPad中实现(但请注意缺少Rx)
void Main()
{
var source=新列表();
对于(inti=0;i<100;i++){source.Add(新项(i));}
//分成十个批次,但只传递项目,而不是临时元组构造。
var batches=source.Select((item,idx)=>new{item,idx})
.GroupBy(tuple=>tuple.idx/10,tuple=>tuple.item);
//一次处理一个批次(串行),但并行(并发)处理该批次的项目。
foreach(批量var)
{
“正在处理批…”Dump();
var results=batch.AsParallel().Select(item=>item.Process());
foreach(结果中的var结果)
{
result.Dump();
}
“已处理批。”.Dump();
}
}
公共类项目
{
私有静态只读随机_rnd=new Random();
私有只读int_id;
公共项目(int id)
{
_id=id;
}
公共int-Id{get{return_-Id;}}
公共双过程()
{
var threadId=Thread.CurrentThread.ManagedThreadId;
Format(“线程上的处理:{0}”,threadId).Dump(Id);
var loopCount=_rnd.Next(100001000000);
SpinWait(loopCount);
返回_rnd.NextDouble();
}
公共重写字符串ToString()
{
返回string.Format(“项:{0}”,_id);
}
}
我很想知道您是否有数据动态问题或反应性消费者问题,但只是将问题“简化”以便于解释。非常简单。窗口(10)将工作转换为10块。合并(1)在单个线程上工作。将这10名学生转化为一个内在的可观察对象。做,呃…做点工作。ObserveOnDispatcher()返回下一位的UI线程。做…嗯…更新工作。最后,订阅内部可观察到的内容。冲洗并重复。再次感谢阿伦。我的疑问是如何释放每10个学生对象资源。你的解释很有帮助,非常感谢。我担心内存不足的问题。请帮帮我。再说一遍,如果不是UI线程,observeondispatcher将替换什么???在这种情况下,如果您不需要封送回UI线程,您可以删除observeondispatcher()。至于内存问题,内存管理环境的要点是内存是GCed的。观察者应在其提出待完成后自行清理。没有承诺…谢谢你的回复。我对RX很陌生,正在努力学习。你们都很有帮助。在这个解决方案中,我看到它只需要10个学生,在这10个学生完成之后,在它覆盖100个学生之前,无法看到任何代码来处理接下来的10个学生。学生列表集合总共包含100名学生。由于所有100名学生并行运行可能会导致内存不足异常,因此我的计划是将学生分为10批运行。请提供帮助。
.Merge(10)
会这样做。想想看
students
.ToObservable()
.Select(student => Observable.Defer(() => Observable.Start(() =>
{
// do the work for this student, then return a Tuple of the student plus any error
try
{
student.DoWork();
return { Student = student, Error = (Exception)null };
}
catch (Exception e)
{
return { Student = student, Error = e };
}
})))
.Merge(10) // let 10 students be executing in parallel at all times
.Subscribe(studentResult =>
{
if (studentResult.Error != null)
{
// handle error
}
studentResult.Student.Dispose(); // if your Student is IDisposable and you need to free it up.
});
void Main()
{
var source = new List<Item>();
for (int i = 0; i < 100; i++){source.Add(new Item(i));}
//Put into batches of ten, but only then pass on the item, not the temporary tuple construct.
var batches = source.Select((item, idx) =>new {item, idx} )
.GroupBy(tuple=>tuple.idx/10, tuple=>tuple.item);
//Process one batch at a time (serially), but process the items of the batch in parallel (concurrently).
foreach (var batch in batches)
{
"Processing batch...".Dump();
var results = batch.AsParallel().Select (item => item.Process());
foreach (var result in results)
{
result.Dump();
}
"Processed batch.".Dump();
}
}
public class Item
{
private static readonly Random _rnd = new Random();
private readonly int _id;
public Item(int id)
{
_id = id;
}
public int Id { get {return _id;} }
public double Process()
{
var threadId = Thread.CurrentThread.ManagedThreadId;
string.Format("Processing on thread:{0}", threadId).Dump(Id);
var loopCount = _rnd.Next(10000,1000000);
Thread.SpinWait(loopCount);
return _rnd.NextDouble();
}
public override string ToString()
{
return string.Format("Item:{0}", _id);
}
}