C#中与MsgWaitForMultipleObjects相当的是什么?

12

我有一个在报表模式下的 Windows 窗体,里面有一个 ListView。对于视图中的每个项,我都需要执行一个长时间运行的操作,其结果是一个数字。

在本机 Win32 中,我会为每个项创建一个工作线程(天真地;当然我不会创建无限数量的线程),然后在线程句柄数组上 MsgWaitForMultipleObjects()。每当计算完成时,线程会发出信号,主 UI 线程就会醒来并更新。与此同时,我们泵送消息,以保持 UI 线程的响应性。

有人能提供一个在 C# 中实现的示例吗?我看过 Monitor 对象,但它似乎不是我想要的或者它是否在阻塞时泵送消息?

谢谢。

编辑:似乎 WaitHandle.WaitAny() 实际上可以泵送消息。请参见cbrumme 的论文关于 CLR 中的消息泵。


你说得对,Monitor不会传递消息。WaitHandle可能是一个更好的选择,但我找不到一个同时也能传递消息的WaitHandle方法。 - itowlson
是的,我也看到了。我并不需要等待多个对象。我只需要等待单个事件或其他什么东西……我只是不想阻塞用户界面。我找到的所有示例都使用 sleep(100) 或类似的方法来强制进行上下文切换,这非常令人沮丧。 - i_am_jorf
我猜问题在于WinForms中的消息循环不是显式的。因此,您无法直接执行MsgWaitX,而是需要某些将引发事件的东西。例如,将每个子任务作为BackgroundWorker运行(并忘记同步原语)。 - itowlson
2个回答

3
让你的主线程创建一个管理线程。你可以使用BackgroundWorker。这个管理线程为ListView中的每个项目启动一个工作线程。这将允许你的用户界面在后台线程处理时仍能响应用户输入而不会挂起。
现在的问题是如何等待每个工作线程完成。不幸的是,我无法找到一种获取System.Threading.Thread对象的线程句柄的方法。我并不是说没有办法做到这一点;我只是没找到。另一个使事情复杂的方面是System.Threading.Thread类是密封的,所以我们不能从中派生出提供某种'句柄'的类。
这就是我使用ManualResetEvent的地方。
假设每个工作线程只是一个线程池线程。管理的BackgroundWorker为ListView中的每个项目创建一个ManualResetEvent对象。当BackgroundWorker启动每个ThreadPool线程时,将ManualResetEvent作为参数传递给QueueUserWorkItem函数。然后,在每个ThreadPool线程退出之前,设置ManualResetEvent对象。 BackgroundWorker线程可以将所有的ManualResetEvent对象放在一个数组中,并使用WaitHandle.WaitXXX函数等待该数组。当每个线程完成时,你可以使用BackgroundWorker的事件更新用户界面,或者你可以使用Control.Invoke()技术来更新用户界面(参见Marc Gravell的答案这里)。
希望这能帮到你。

如果我无法找到消息泵等待的方法,这似乎是一个合理的方法。我真的很难相信没有这样的方法。如果我找到了什么,我会更新这个线程。 - i_am_jorf

2
长时间运行的活动对象,在您的情况下,我认为是最好的选择。主线程调用代理(活动对象)。代理将调用方法转换为消息,并将此消息发送到队列中。代理返回给调用者一个未来对象(它是未来结果的引用)。调度程序逐个出队消息,并在其他线程(工作线程)中真正执行您的任务。当工作线程完成任务时,它会更新未来对象的结果或调用回调方法(例如,更新您的UI)。调度程序可以有许多工作线程以同时执行多个任务。您可以参考这个article(带有示例)关于长时间运行的活动对象模式。

请提供一些信息,而不仅仅是信息链接。 - John Saunders
嗯,看起来为了解决这么简单的问题需要写很多代码。我的意思是,如果我想的话,我总可以调用自己的COM对象。如果C#框架还不够成熟,不能让我轻松地做到这一点,我可以回到编写win32代码的时代。虽然我相信肯定有更简单的方法可以实现这个问题。 - i_am_jorf
是的,你说得对。这是MSDN上的一个例子: connection1.Open(); SqlCommand command1 = new SqlCommand(commandText1, connection1); IAsyncResult result1 = command1.BeginExecuteNonQuery(); WaitHandle waitHandle1 = result1.AsyncWaitHandle; connection2.Open(); ... WaitHandle[] waitHandles = { waitHandle1, waitHandle2, waitHandle3 }; bool result = WaitHandle.WaitAll(waitHandles, 60000, false); - garik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接