windows 一次性处理大量数据时避免窗口中的“(无响应)”标签

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/402832/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:46:38  来源:igfitidea点击:

Avoiding "(Not Responding)" label in windows while processing lots of data in one lump

c++windowsmfc

提问by Foo42

I occasionally need to process a large amount of data from one package off the network, which takes sufficiently long that when the user tries to interact with the application windows adds the "(Not Responding)" string to the window title. I am aware this is because the processing is being done within a call to handle a message (some way up the stack) and therefore is blocking the message pump. I'm also aware the ideal way to deal with this is to process the data asynchronously in a separate thread so the pump can continue running, however this is a LARGE desktop application which is single threaded from top to toe and safely spinning this processing off is not feasible in our time frame.

我偶尔需要从网络外的一个包中处理大量数据,这需要足够长的时间,以至于当用户尝试与应用程序交互时,窗口会在窗口标题中添加“(无响应)”字符串。我知道这是因为处理是在处理消息的调用中完成的(堆栈的某种方式),因此阻塞了消息泵。我也知道处理这个问题的理想方法是在单独的线程中异步处理数据,以便泵可以继续运行,但是这是一个大型桌面应用程序,它从头到脚都是单线程的,并且可以安全地关闭此处理在我们的时间范围内是不可行的。

So with that in mind, is there by any chance a way I can at least avoid the "not responding" moniker (which to most users reads as "has crashed") by telling windows my application is about to be busy before I begin the work? I believe there is something along these lines when responding to a request to close, one can keep asking windows for more time to avoid it proclaiming that your not "closing in a timely fashion"

因此,考虑到这一点,是否有任何机会至少可以避免“无响应”绰号(大多数用户读作“已崩溃”),方法是在我开始之前告诉 Windows 我的应用程序将要忙工作?我相信在响应关闭请求时有一些类似的东西,人们可以继续要求窗户更多时间以避免它宣称你没有“及时关闭”

I should add this is a C++ MFC application.

我应该补充一点,这是一个 C++ MFC 应用程序。

回答by Frederick The Fool

I don't think the Windows API can help you here.

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

Alternatively, how about showing a dialog box with a progress bar and make it run in a separate thread?

或者,如何显示一个带有进度条的对话框并让它在单独的线程中运行?

A text like "This operation may take half an hour" on the dialog box may be appropriate too.

对话框中类似“此操作可能需要半小时”之类的文字也可能合适。

回答by SmacL

Ok, firstly I upvoted Frederick's post because like it or not, the second thread is probably the best way to go.

好的,首先我赞成 Frederick 的帖子,因为不管喜欢与否,第二个线程可能是最好的方法。

However, if you really don't want to go down this road, you could manually pump the message queue within your apps inner loop. Something like this;

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

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

This is actually a piece of code I used prior to rewriting something similar as a seperate thread. Basically I have a look at the queue, filter out unwanted messages, and post on the rest. It works to an extent, but caused some occasional nasty side effects, hence the rewrite.

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

回答by adzm

You don't have to actually do anything with the messages from PeekMessage. Just call PeekMessage, you don't even have to remove anything from the queue or process it. As long as it is called every 5 seconds or so, it will cause windows to think the process is still responsive.

您实际上不必对来自 PeekMessage 的消息执行任何操作。只需调用 PeekMessage,您甚至不必从队列中删除任何内容或对其进行处理。只要每5秒左右调用一次,就会导致windows认为进程还在响应。

An alternative idea is to have a separate process/thread that will appear in the notification tray and inform the user that the process is busy waiting for an internal operation to complete. You'll see these in the later versions of Visual Studio, SQL Server Management Studio, etc.

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

回答by DuckMaestro

Win32 has a method for this in user32.dll.

Win32 在user32.dll.

DisableProcessWindowGhosting()

DisableProcessWindowGhosting()

Disables the window ghosting feature for the calling GUI process. Window ghosting is a Windows Manager feature that lets the user minimize, move, or close the main window of an application that is not responding.

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

In addition to the above documented behavior, I also verified here (in a C# application) that this Win32 call also prevents the Not Respondinglabel from appearing on the window as desired.

除了上述记录的行为之外,我还在此处(在 C# 应用程序中)验证了此 Win32 调用还可以防止“未响应”标签按需要出现在窗口上。

I found this via the C# answer to similar question here: https://stackoverflow.com/a/15380821/29152.

我通过 C# 对类似问题的回答找到了这个:https: //stackoverflow.com/a/15380821/29152

回答by user49913

If you fork off a thread you're most likely worried about some other user action happening which may depend on the result of the long running operation (yeah, concurrency). So expanding on what Fredrick said, if you do spin off a new thread and put up a progress bar, you could lock the focus onto the progress bar to stop a user from interacting with the rest of the application. That should be enough to implement a really simple second thread without really having to worry about concurrency because you're essentially locking out the rest of the app by disabling user interation.

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

回答by Rune

You'll need to interleave the processing with message handling somehow. If threads are out of the question, you might want to look at splitting the processing into multiple phases. One way to do this is to do some processing when you first receive the packet, then post a message to the application saying "continue processing here". When the application receives the "continue processing here" message, it will do some more processing, and either send another "continue processing here" message or finish up.

您需要以某种方式将处理与消息处理交织在一起。如果线程不成问题,您可能需要考虑将处理分成多个阶段。一种方法是在您第一次收到数据包时进行一些处理,然后向应用程序发布一条消息,说“在此处继续处理”。当应用程序收到“在此处继续处理”消息时,它会进行更多处理,然后发送另一个“在此处继续处理”消息或完成处理。

There are a couple of considerations though:

但有几个考虑因素:

  1. You need to make sure that the state of the application is consistent every time you post a message to yourself and defer to the message loop, as other message handling might happen in the mean-time. This can be done e.g. by only changing the state in the final processing phase.
  2. Another packet might arrive while you are still processing the first packet. If changing the order of processing would be bad for the application, you could handle this by e.g. posting a "remind me to process this packet later" message when this happens.
  1. 您需要确保每次向自己发布消息并遵循消息循环时应用程序的状态是一致的,因为在此期间可能会发生其他消息处理。这可以通过例如仅在最终处理阶段改变状态来完成。
  2. 当您仍在处理第一个数据包时,另一个数据包可能会到达。如果更改处理顺序对应用程序不利,您可以通过例如在发生这种情况时发布“提醒我稍后处理此数据包”消息来处理此问题。

I don't know whether this would be feasible within the design of your application, but it would be one way to solve the problem.

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

回答by Mark Ransom

If you are unwilling to spawn a worker thread, but you can break the long-running task down into smaller parts, you can do the processing in MFC's CWinApp::OnIdle. This function gets called from within the message pump loop whenever there are no Windows messages waiting. As long as the work you do in each OnIdlecall is sufficiently short, you keep your app responsive.

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

回答by Aardvark

Assuming that it is the processing of the data that is taking up all the time and not the receiving (and you're serious about avoiding a thread - which is fine IMOHO) of the data you could:

假设它一直在处理数据而不是接收数据(并且您很认真地避免线程 - 这很好 IMOHO),您可以:

  1. In the function that you are currently handling the message, create a modal dialog that shows a "please wait" message (or make it hidden, small, whatever...). Copy (or send a pointer, etc...) the data you're processing to a member variable of that dialog.
  2. In the modal dialog post a user-defined message to yourself to process the data.
  3. In the dialog's message handler, handle one "unit" of work. Keep track what the next "unit" of work is. Post the same message again.
  4. Repeat this post-message "loop" until done. Close your dialog.
  1. 在您当前正在处理消息的函数中,创建一个显示“请稍候”消息的模式对话框(或将其隐藏、缩小,等等……)。将您正在处理的数据复制(或发送指针等)到该对话框的成员变量。
  2. 在模态对话框中,向自己发布一条用户定义的消息以处理数据。
  3. 在对话框的消息处理程序中,处理一个“单元”的工作。跟踪下一个“工作单元”是什么。再次发布相同的消息。
  4. 重复此后消息“循环”直到完成。关闭对话框。

The nature of the modal dialog will keep you're application "responsive", with minimal interruption or change to how the application worked previously. Reentrancy can be a problem with modal loops, especially if any of this is involved with a WM_PAINT message. (anyone ever assert inside painting code? good times, good times...)

模态对话框的性质将使您的应用程序保持“响应性”,而对应用程序以前的工作方式的中断或更改最少。重入可能是模态循环的一个问题,特别是如果其中任何一个与 WM_PAINT 消息有关。(有人在绘画代码中断言吗?好时光,好时光......)

The dialog could even have a cancel button if you'd like.

如果您愿意,该对话框甚至可以有一个取消按钮。

回答by KBell

I encountered the exactsame problem. Since I dont consider the other answers appealing/straightforward I decided to post this.

我遇到了完全相同的问题。由于我不认为其他答案具有吸引力/直截了当,我决定发布此内容。

Short description and some context: I am saving data from a grid into a database, and this process can take a while. So I changed the saving method to an asynchronous method and had the same problem.

简短描述和一些上下文:我正在将数据从网格保存到数据库中,这个过程可能需要一段时间。所以我将保存方法更改为异步方法并遇到了同样的问题。

Then I came up with a simple solution:

然后我想出了一个简单的解决方案:

//__ENABLE OR DISABLE MAIN DIALOG
void CMFCApplication1Dlg::enableMainDlg(bool enable)
{ 
    this->EnableWindow(enable);
}

When starting the asynchronous method, I disable the main dialog. This prevents the user from interacting with the main dialog (like starting another saving process which could result in thousands of SQL error messages if I wouldn't check if the saving process is already running...)

启动异步方法时,我禁用了主对话框。这可以防止用户与主对话框交互(例如启动另一个保存过程,如果我不检查保存过程是否已经在运行,则可能会导致数千条 SQL 错误消息......)

When the saving process is finished, I re-enable the main dialog. Works like a charm, I hope this helps

保存过程完成后,我重新启用主对话框。像魅力一样工作,我希望这会有所帮助

回答by Suit Boy Apps

One way to overcome your application from becoming unresponsive you need to tell the application to process messages from windows. When you are in your loop you can call

一种克服应用程序无响应的方法,您需要告诉应用程序处理来自 Windows 的消息。当你在你的循环中时,你可以打电话

Application->ProcessMessages();