C# 4.0 这是否可能使用反应式框架?

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轮完成并释放

我的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);
        }
    }