Я бы предпочел посмотреть, есть ли разумный способ справиться с этим без использования второго потока из-за специфики этого приложения (изначально приложение DOS с большим количеством глобальных статических переменных, преобразованных в Windows / MFC, но не предназначенных с нуля, чтобы быть многопоточным - просто сделать его осведомленным о нескольких документах было большой задачей).
Рассматриваемое приложение пытается выполнить очень сложную вычислительную операцию, побочным эффектом которой является изменение текущего документа. Он хочет иметь возможность итеративно обновлять главное окно и окна документа по мере выполнения процесса.
Выполнение простого подхода: циклический просмотр рабочих элементов, выдача чертежа в окне документа, изменение базовых данных документа и обновление строки состояния с текстом статуса, указывающим x of y complete, обычно работает. Но иногда приложение перестает обновлять как строку состояния, так и окно документа (представление), пока все задание не будет завершено, а затем все обновляется сразу.
Так что в коде нет никаких зависаний. то есть оно никогда не завершается. Это просто вопрос невозможности обработки оконных сообщений в течение всего времени (так как это по большей части однопоточное приложение).
Я думал, что очевидным подходом было бы поместить цикл PeekMessage () в цикл задач для отправки любых накопленных сообщений. Я предполагал, что это является причиной того, что визуальные обновления перестают происходить: должно быть сообщение Windows либо в представлении, либо в главном фрейме, либо в очереди сообщений потока, блокирующей прямые обновления экрана.
Однако, возможно, проблема в чем-то другом?
Несмотря на это, для нашего приложения просто плохой идеей было бы игнорировать очередь сообщений в течение, как правило, времени обработки (пользователь будет воспринимать приложение как «переставшее отвечать», если он запрашивает диспетчер задач только из-за отсутствия обработки нашей очереди сообщений).
Однако следующий цикл становится бесконечным:
// dispatch the messages until we're out of them again...
for (MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); )
{
TraceMessage(msg.message, msg.wParam, msg.lParam);
if (msg.message == WM_QUIT)
return;
}
ПРИМЕЧАНИЕ: TraceMessage () просто выводит удобную для человека трассировку в окно отладчика, что это за сообщение и аргументы. В данном случае это WM_PAINT.
Таким образом, сообщение рисования, даже несмотря на то, что оно запрашивается для удаления, кажется, всегда находится в очереди (или новые создаются каким-то образом бесконечно).
Вопросы:
Могу ли я все неправильно понять, и причина, по которой наши приложения перестают обновлять строку состояния и вид, это что-то еще?
Есть ли лучший подход к длительной интенсивной работе процессора или дискового ввода-вывода, чем размещение цикла PeekMessage в цикле задач (который не требует перестройки всего приложения, чтобы сделать его более многопоточным) дружественный)
Решение, с которым я собираюсь ...
void DoSomethingLengthy()
{
CWaitCursor wait;
// disable our application windows much as if we were running a modal dialog
// in order to lock out the user from interacting with us until we're doing doing this thing
AfxGetMainWnd()->BeginModalState();
while (bMoreWorkToDo)
{
// empty any generated / received thread & window messages
for (MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD); )
AfxPumpMessage();
// for some reason, our cursor reverts to the normal pointer
// so force ourselves to continue to have the wait-cursor until we're really done
AfxGetMainWnd()->RestoreWaitCursor();
// here's where we do one work-item
// ...
}
// restore our prior state
AfxGetMainWnd()->EndModalState();
}
Да, это очень старая технология, и не обязательно лучший подход. Однако это возможно и очень полезно для этого контекста. У нас уже есть сложное приложение, которое использует механику OnIdle для множества целей, что делает его менее привлекательным в качестве возможного подхода.