Избегать метки «(не отвечает)» в окнах при обработке большого количества данных одним комком - PullRequest
12 голосов
/ 31 декабря 2008

Мне иногда нужно обрабатывать большой объем данных из одного пакета вне сети, что занимает достаточно много времени, чтобы при попытке пользователя взаимодействовать с окнами приложения в заголовок окна добавлялась строка «(не отвечает)». Я знаю, что это потому, что обработка выполняется в вызове для обработки сообщения (каким-то образом вверх по стеку) и, следовательно, блокирует рассылку сообщений. Я также знаю, что идеальный способ справиться с этим - это асинхронно обрабатывать данные в отдельном потоке, чтобы насос мог продолжать работу, однако это БОЛЬШОЕ настольное приложение, которое является однопоточным сверху вниз и безопасно отбрасывает эту обработку невозможно в наше время.

Итак, с учетом этого, есть ли какой-нибудь шанс, которым я могу, по крайней мере, избежать "не отвечающего" прозвища (которое большинство пользователей читает как "сбой"), сообщая windows, что мое приложение собирается быть занятым до того Я начинаю работу? Я полагаю, что что-то в этом роде, когда отвечаешь на запрос о закрытии, можно продолжать просить у окон больше времени, чтобы избежать объявления о том, что ты не "закрываешься своевременно"

Я должен добавить, что это приложение C ++ MFC.

Ответы [ 11 ]

16 голосов
/ 31 декабря 2008

Я не думаю, что Windows API может вам здесь помочь.

В качестве альтернативы, как насчет отображения диалогового окна с индикатором выполнения и его запуска в отдельном потоке?

Может также подойти текст типа «Эта операция может занять полчаса» в диалоговом окне.

10 голосов
/ 31 декабря 2008

Хорошо, во-первых, я проголосовал за пост Фредерика, потому что, нравится вам это или нет, второй поток, вероятно, лучший путь.

Однако, если вы действительно не хотите идти по этому пути, вы можете вручную накачать очередь сообщений во внутреннем цикле ваших приложений. Как то так;

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

Это на самом деле кусок кода, который я использовал перед переписыванием чего-то похожего на отдельный поток. В основном я смотрю на очередь, отфильтровываю нежелательные сообщения и публикую остальные. Это работает до некоторой степени, но вызвало некоторые неприятные побочные эффекты, следовательно, переписать.

9 голосов
/ 01 января 2009

Вам не нужно ничего делать с сообщениями из PeekMessage. Просто позвоните в PeekMessage, вам даже не нужно ничего удалять из очереди или обрабатывать. Пока он вызывается каждые 5 секунд или около того, окна будут думать, что процесс все еще реагирует.

Альтернативной идеей является создание отдельного процесса / потока, который будет отображаться в области уведомлений и информировать пользователя о том, что процесс занят, ожидая завершения внутренней операции. Вы увидите это в более поздних версиях Visual Studio, SQL Server Management Studio и т. Д.

6 голосов
/ 24 июля 2015

Win32 имеет метод для этого в user32.dll.

DisableProcessWindowGhosting()

Отключает функцию окошек для вызывающего процесса GUI. Оконное окошко - это функция диспетчера Windows, которая позволяет пользователю свернуть, переместить или закрыть главное окно приложения, которое не отвечает.

В дополнение к вышеописанному документированному поведению я также проверил здесь (в приложении C #), что этот вызов Win32 также предотвращает появление метки Not Responding в окне по желанию.

Я нашел это через ответ C # на похожий вопрос здесь: https://stackoverflow.com/a/15380821/29152.

5 голосов
/ 31 декабря 2008

Если вы отключите поток, вы, скорее всего, будете беспокоиться о каких-то других пользовательских действиях, которые могут зависеть от результата длительной операции (да, параллелизма). Итак, расширяя сказанное Фредриком, если вы раскручиваете новый поток и устанавливаете индикатор выполнения, вы можете зафиксировать фокус на индикаторе выполнения, чтобы пользователь не взаимодействовал с остальной частью приложения. Этого должно быть достаточно для реализации действительно простого второго потока, не беспокоясь о параллелизме, потому что вы по сути блокируете остальную часть приложения, отключая взаимодействие с пользователем.

4 голосов
/ 31 декабря 2008

Вам нужно как-то чередовать обработку с обработкой сообщений. Если о потоках не может быть и речи, вы можете посмотреть на разделение обработки на несколько этапов. Один из способов сделать это - выполнить некоторую обработку при первом получении пакета, а затем отправить в приложение сообщение с надписью «продолжить обработку здесь». Когда приложение получит сообщение «продолжить обработку здесь», оно выполнит еще некоторую обработку и либо отправит еще одно сообщение «продолжить обработку здесь», либо завершит работу.

Хотя есть несколько соображений:

  1. Вам необходимо следить за тем, чтобы состояние приложения было постоянным каждый раз, когда вы отправляете сообщение себе и откладываете на цикл обработки сообщений, поскольку в это время может произойти другая обработка сообщений. Это можно сделать, например, изменяя только состояние на завершающем этапе обработки.
  2. Другой пакет может прибыть, пока вы еще обрабатываете первый пакет. Если изменение порядка обработки будет плохо для приложения, вы можете справиться с этим, например, когда это произойдет, появится сообщение «напомни мне обработать этот пакет позже».

Я не знаю, будет ли это возможно при разработке вашего приложения, но это был бы один из способов решения проблемы.

2 голосов
/ 14 октября 2015

Если вы не хотите создавать рабочий поток, но вы можете разбить долгосрочную задачу на более мелкие части, вы можете выполнить обработку в MFC CWinApp::OnIdle. Эта функция вызывается из цикла прокачки сообщений всякий раз, когда нет ожидающих сообщений Windows. Пока работа, которую вы выполняете в каждом OnIdle вызове, достаточно коротка, ваше приложение будет отзывчивым.

1 голос
/ 30 января 2009

Предполагая, что обработка данных занимает все время, а не получение (и вы серьезно относитесь к тому, чтобы избежать потока - что хорошо, IMOHO) данных, которые вы могли бы:

  1. В функции, с которой вы в данный момент обрабатываете сообщение, создайте модальное диалоговое окно, которое показывает сообщение «пожалуйста, подождите» (или сделайте его скрытым, маленьким, как угодно ...). Скопируйте (или отправьте указатель и т. Д.) Данные, которые вы обрабатываете, в переменную-член этого диалога.
  2. В модальном диалоговом окне опубликуйте пользовательское сообщение для обработки данных.
  3. В обработчике сообщений диалогового окна обрабатывается одна «единица» работы. Следите за следующей «единицей» работы. Опубликуйте то же сообщение еще раз.
  4. Повторяйте эту петлю пост-сообщения, пока не закончите. Закройте диалог.

Характер модального диалога будет держать ваше приложение "отзывчивым" с минимальным прерыванием или изменением того, как приложение работало ранее. Повторный вход может быть проблемой с модальными циклами, особенно если что-либо из этого связано с сообщением WM_PAINT. (кто-нибудь когда-нибудь утверждал в коде рисования? хорошие времена, хорошие времена ...)

В диалоге даже может быть кнопка отмены, если хотите.

0 голосов
/ 05 марта 2019

У меня была похожая проблема с приложением win32, которое ожидало ответа от веб-службы с использованием API cpprest (Касабланка). Мое решение состояло в том, чтобы создать событие и поток, который делает только ожидание API cpprest, а затем освободить поток, как только он получит сигнал:

DWORD WINAPI WaitForCasablanca(LPVOID n)
{

// Get the handler to the event for which we need to wait in 
//    this thread.
 HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, false, "MyEvent");
if (!hEvent) { return -1; }
// Loop through and wait for an event to occur

    // Wait for the Event
    WaitForSingleObject(hEvent, INFINITE);
    //    No need to Reset the event as its become non signaled as soon as
    //    some thread catches the event.

CloseHandle(hEvent);

return 0;}

BOOL WINAPI DlgProc(HWND hDlg, UINT message, WPARAM,wParam, LPARAM lParam) ...
  HANDLE     hEvent = CreateEvent(NULL, false, false,        "MyEvent");//create an event that will wait for casablanca ro authenticate
if (!hEvent) return -1;
            //    Create a Thread Which will wait for the events to occur
  DWORD Id;
  HANDLE hThrd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WaitForCasablanca, 0, 0, &Id);
            if (!hThrd) { CloseHandle(hEvent); return -1; }
   makeCasablancaRequest(...);
 SetEvent(hEvent); //casablanca has finished signal the event to terminate

    WaitForSingleObject(hThrd, INFINITE); //wait for thread to die

            CloseHandle(hThrd);
            CloseHandle(hEvent);
            ...}

Это избавило меня от сообщения "программа не отвечает". Я полагаю, что проблема в том, что код, который получает данные, работает и в потоке - только основная программа не знает этого - поэтому, что касается системы, основная программа работает вхолостую. Вам нужно событие и поток, который ожидает события, чтобы сообщить системе, что программа ожидает данные. Я получил код из этого урока: Как использовать объект ядра событий WIN32

0 голосов
/ 02 апреля 2018

Один из способов избавиться от приложения, если оно перестает отвечать на запросы, - указать приложению обрабатывать сообщения из окон. Когда вы находитесь в своей петле, вы можете позвонить

Application->ProcessMessages();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...