我在报告模式下有一个带有ListView的Windows窗体。对于视图中的每个项目,我需要执行一个长时间运行的操作,其结果是一个数字。
我在本机win32中执行此操作的方法是为每个项创建一个工作线程(天真地;当然我不会创建无限数量的线程),然后在线程句柄数组上创建MsgWaitForMultipleObjects()。当每个计算完成时,线程信号和主UI线程唤醒并更新。与此同时,我们提取消息,以便UI线程保持响应。
任何人都可以提供一个如何在C#中运行的示例吗?我看过Monitor对象,它似乎不是我想要的 - 或者是否在阻塞时抽取消息?
感谢。
编辑:似乎WaitHandler.WaitAny()可能实际上是在发送消息。有关CLR中消息提取的信息,请参阅cbrumme's treatise。
答案 0 :(得分:2)
我认为,长期运行的活动对象是您的最佳选择。主线程调用代理(活动对象)。代理将调用方法转换为消息,此消息将转到队列。代理向未来对象返回调用者(它是对未来结果的引用)。调度程序逐个使消息出列,并在其他线程(工作线程)中重新执行您的任务。当工作线程完成一个任务时,它会更新未来对象的结果或调用回调方法(例如,更新你的UI).Dispather可以有许多工作线程来同时执行更多的任务。
您可以看到关于长时间运行的活动对象模式的 article (带示例)。
答案 1 :(得分:2)
让你的主线程创建一个经理线程。您可以使用BackgroundWorker来实现此目的。此管理器线程为ListView中的每个项启动工作线程。这将允许您的UI在后台线程处理时不会挂起而继续响应用户输入。
现在,问题是如何等待每个工作线程完成。不幸的是,我一直无法找到一种方法来获取System.Threading.Thread
个对象的线程句柄。我不是说没有办法做到这一点;我还没找到一个。另一个复杂的方面是System.Threading.Thread
类是密封的,所以我们无法从中得到某种“句柄”。
这是我使用ManualResetEvent的地方。
假设每个工作线程只是一个ThreadPool
线程。管理BackgroundWorker
为ListView中的每个项创建一个ManualResetEvent
对象。当BackgroundWorker
启动每个ThreadPool
主题时,将ManualResetEvent
作为参数传递给QueueUserWorkItem
function。然后,在每个ThreadPool
线程退出之前,设置ManualResetEvent
对象。
BackgroundWorker
线程然后可以将所有ManualResetEvent
个对象放入一个数组中,并使用WaitHandle.WaitXXX
functions等待该数组。每个线程完成后,您可以使用BackgroundWorker
的事件来更新UI,或者您可以使用Control.Invoke()
技术更新UI(请参阅Marc Gravell的回答here)。
希望这有帮助。