在一次处理大量数据的同时,避免在窗口中出现“(不响应)”标签

我偶尔需要从networking上的一个包中处理大量数据,这需要足够长的时间,当用户尝试与应用程序窗口交互时,将“(Not Responding)”string添加到窗口标题。 我知道这是因为处理是在一个调用中处理一个消息(堆栈的某种方式),因此阻止消息泵。 我也知道处理这个问题的理想方法是在一个单独的线程中asynchronous处理数据,所以泵可以继续运行,但是这是一个大型的桌面应用程序,从顶部到脚趾单线程安全地旋转这个处理在我们的时间框架内是不可行的。

所以,考虑到这一点,有没有办法通过告诉Windows我的应用程序即将忙于开始之前,我至less可以避免“不响应”的绰号(对大多数用户来说这是“崩溃”)工作? 我相信,在回应closures请求时,有这样一个问题,你可以不停地问窗口,以避免它宣布你没有“及时closures”

我应该添加这是一个C ++ MFC应用程序。

我不认为Windows API可以帮助你。

或者,如何显示一个对话框的进度条,并使其运行在一个单独的线程?

对话框中的“此操作可能需要半小时”这样的文本也可能是适当的。

好吧,首先我赞成弗雷德里克的职位,因为不管你喜不喜欢,第二个线程可能是最好的选择。

但是,如果你真的不想走这条路,你可以在应用程序的内部循环中手动抽取消息队列。 像这样的东西;

int Refresh() { MSG msg; if (PeekMessage (&msg, NULL, 0, 0,PM_NOREMOVE)) if ((msg.message == WM_QUIT) ||(msg.message == WM_CLOSE) ||(msg.message == WM_DESTROY) ||(msg.message == WM_NCDESTROY) ||(msg.message == WM_HSCROLL) ||(msg.message == WM_VSCROLL) ) return(1); if (PeekMessage (&msg, NULL, 0, 0,PM_REMOVE)) { TranslateMessage (&msg); DispatchMessage (&msg); } return(0); } 

这实际上是我在重写类似一个单独的线程之前使用的一段代码。 基本上我看看队列,过滤掉不需要的消息,然后发布。 它在一定程度上起作用,但造成了一些偶然的讨厌的副作用,因此被重写。

您不必实际使用来自PeekMessage的消息。 只要调用PeekMessage,你甚至不需要从队列中删除任何东西或处理它。 只要每5秒左右调用一次,就会导致窗口认为该进程仍然是响应式的。

另一个想法是有一个单独的进程/线程将出现在通知托盘中,并通知用户该进程正忙于等待内部操作完成。 您将在Visual Studio,SQL server Management Studio等的更高版本中看到这些内容。

如果你分离一个线程,你很可能会担心一些其他的用户行为,这可能取决于长时间运行的结果(是的,并发性)。 因此,扩展Fredrick所说的话,如果你分离出一个新的线程并建立一个进度条,你可以将焦点锁定在进度条上,以阻止用户与其他应用程序交互。 这应该足以实现一个非常简单的第二个线程,而不必担心并发,因为实际上通过禁用用户互动来锁定应用程序的其余部分。

您需要以某种方式交错处理消息处理。 如果线程不在问题中,您可能需要考虑将处理分成多个阶段。 一种方式是在您首次接收数据包时进行一些处理,然后向应用程序发送一条消息,指出“在此处继续处理”。 当应用程序收到“继续处理此处”消息时,它将做更多的处理,并发送另一个“继续处理此处”消息或完成。

有几个考虑因素:

  1. 每次向自己发布消息时,都需要确保应用程序的状态是一致的,并遵循消息循环,因为其他消息处理可能同时发生。 这可以通过例如仅改变最终处理阶段中的状态来完成。
  2. 当你还在处理第一个数据包时,另一个数据包可能会到达。 如果改变处理顺序对于应用程序是不好的,你可以通过例如发送一个“提醒我稍后处理这个数据包”的消息来处理。

我不知道在你的应用程序的设计中这是否可行,但这是解决问题的一种方法。

Win32在user32.dll有这个方法。

DisableProcessWindowGhosting()

禁用调用GUI进程的窗口重影功能。 窗口重影是Windows管理器功能,可让用户最小化,移动或关闭未响应的应用程序的主窗口。

除了上面记录的行为之外,我还在这里(在C#应用程序中)验证了此Win32调用还可以防止窗口上显示Not Responding标签。

我通过C#的答案找到了类似的问题在这里: https : //stackoverflow.com/a/15380821/29152 。

如果你不愿意产生一个工作线程,但是你可以将长时间运行的任务分解成更小的部分,你可以在MFC的CWinApp::OnIdle 。 只要没有Windows消息在等待,该函数就会从消息泵循环中调用。 只要您在每个OnIdle通话中所做的工作都足够短,您就可以保持应用程序的响应。

假设它是处理的数据,一直占用,而不是接收(你是认真的避免一个线程 – 这是罚款IMOHO)的数据,你可以:

  1. 在当前正在处理消息的函数中,创建一个显示“请稍候”消息的模式对话框(或者将其隐藏,小,不管…)。 复制(或发送一个指针等等)你正在处理的数据到该对话框的成员变量。
  2. 在模式对话框中发布用户自定义消息来处理数据。
  3. 在对话框的消息处理程序中,处理一个“单元”的工作。 跟踪下一个“工作单位”是什么。 再次发布相同的消息。
  4. 重复这个后消息“循环”,直到完成。 关闭你的对话框。

模态对话的本质将使您的应用程序“响应”,以最小的中断或改变以前的应用程序的工作。 重入是模式循环的一个问题,特别是如果任何一个涉及WM_PAINT消息的话。 (任何人都会在绘画代码中坚持自己的美好时光,美好时光……)

对话框甚至可以有一个取消按钮,如果你想。