并行处理所需的设计思想
本文关键字:设计思想 并行处理 | 更新日期: 2023-09-27 17:57:38
我有一系列计算需要处理——计算和运行顺序都由用户在UI上定义。
如果他们只是一个接一个地跑,就不会太难了。然而,一些计算需要同时处理,并且所有计算必须能够在任何时候单独暂停。我还需要能够随时重新安排订单或添加要处理的新计算。因此,无论我做什么,都必须有足够的灵活性来处理这个问题。
在UI上,想象一个用户控件的列表框(如果你喜欢的话,是一个队列),每个用户控件都显示计算的名称和一个暂停按钮。在处理过程中,我可以随时将计算添加到此列表中。
最好的方法是什么
我应该在自己的线程中运行每个计算吗?如果是,我应该如何存储正在运行的进程的列表?如何将队列传递给计算处理器?我如何确保每次队列更改(新排序或新计算)时,计算处理器都会意识到这一点?
我最初的想法是:
CalcProcessor class
CalcCalculation class
在CalcProcessor
中有CalcCalculations
中的2个Lists
。一个是UI上显示的"队列"(也许是指向它的指针?或者其他确保它实时更新的方式),另一个是当前正在运行的计算列表。
不知怎的,我需要让CalcCalculation
在它自己的线程中运行来处理计算,并能够处理任何暂停事件。因此,我需要某种方式将UI中按下的Pause按钮的信息传输到CalcProcessor
对象,然后传输到正确的CalcCalculation
。
编辑以回应David Hope:
谢谢你的回复。
-
是的,有n个计算,但由于可以在UI上添加更多的计算来处理,这一点随时可能发生变化。
-
他们不需要在中共享数据。应用程序中会有一个设置来指定应该同时运行的数量(例如,在任何给定时间运行10个,例如队列中的前10个,当1完成时,队列中的下一个计算将开始处理)。
-
计算将包括从某个数据源(可以是数据库或文件)中获取数据,并对其进行分析和计算。当我说计算需要暂停时,我的意思不是暂停线程。。。我的意思是(例如,由于我还没有编写应用程序的这一部分),如果它正在从数据库中逐行读取,并在处理完当前行时暂停进行一些实时计算。。。当UI上的暂停按钮被取消点击时,继续操作——这可以通过while(notPaused)循环这样的原始循环来完成,前提是我可以将暂停信息从UI中获取到线程中。
这里有几个问题:
如何同步UI和模型
我想你把这个倒过来了。您的模型不应该有一个指向您在UI中显示的队列的"指针"。相反,队列应该在您的模型中,并且您应该将数据绑定与INotifyPropertyChange
和ObservableCollection
一起使用,以在UI上显示队列。(至少在WPF中是这样做的。)
通过这种方式,您可以直接从模型中操作队列,它将自动显示在UI上。
如何启动和监控计算
我认为Task
是这方面的理想选择。您可以使用Task.Factory.StartNew()
启动Task
。由于执行Task
似乎需要很长时间,因此可以考虑使用TaskCreationOptions.LongRunning
。您也可以使用Task
来确定计算何时完成(或者是否因异常而失败)。
如何暂停正在运行的计算
您可以使用ManualReserEventSlim
。通常情况下,它会被设置,但如果你想暂停正在运行的Task
,你需要Reset()
。计算需要定期调用该事件的Wait()
。如果没有线程计算的配合,就不可能合理地暂停正在运行的线程。
如果您使用的是C#5.0,那么更好的方法是使用类似PauseToken
的东西。
在Framework 4.5中,这里的答案是Async API,它消除了管理线程的需要。有关详细信息,请查看async/await关键字。
从更广泛的角度来看,"CalcProcessor"类是个好主意,但我认为Task对象足以取代"CalcCalculation"类。处理器可以简单地拥有一个可枚举的任务。如果需要,Processor可以公开用于管理队列的方法,并返回有关其状态的信息。当应用程序最终达到必须具有结果的状态时,可以使用AwaitAll方法阻塞CalcProcessor的线程,直到所有任务完成。
如果没有更多关于实际目标的信息,很难给出更好的建议。
您可以使用Observer Pattern在UI上显示结果,并将更改顺序返回到Processor。状态和命令模式将帮助您开始、暂停和取消计算。这些模式在设计方面对您的问题有很好的答案。并发仍然是一个问题,它们不能解决多线程问题,但为管理线程开辟了一条更容易的道路。
我建议您对问题的分析还不够深入,这就是您感到沮丧的原因。
你需要从小处着手,从小处着手。你提到了,但没有定义你的实际需求,但它们似乎是…
- 需要能够跑步吗?N计算
- 有些需要同时运行(这是否意味着它们共享数据,如果是,您将如何共享数据)
- 必须能够暂停计算(不要使用Thread.Shupend,因为它可能会使线程处于不稳定状态,尤其是在共享数据时),因此您需要在每次计算中建立暂停点。还需要考虑如何将暂停/取消暂停传达给计算
就方法而言,有几个需要考虑。。。
线程是一个显而易见的选择,但也需要小心处理(启动、暂停、停止等)
您也可以使用BackGroundWorker或Parallel.For Each
BackGroundWorker包含用于取消工作人员并提供进度的框架(这可能很有用)。
我建议开始使用BackGroundWorker,可能会将其子类化,以添加您需要的暂停/恢复功能。确定您将如何管理数据共享(至少使用锁来防止同时访问)。
你可能会发现BackGroundWorker限制性太强,需要使用Threads,但我通常可以避免它
如果你发布了更明确的要求,或者你尝试过但没有成功的样本,我很乐意提供更多帮助。
对于队列,您可以使用堆数据结构(优先级队列)。这将有助于确定任务的优先级。此外,您应该使用线程池进行有效的计算。试着把你的任务分成几个小部分。