MFC делает цикл while

Я делаю систему измерения, и у меня есть цикл do while в моей программе. В цикле он проверяет, движется ли какой-либо двигатель, прежде чем он сможет выполнить измерение, поскольку он требует, чтобы он достиг целевого положения, прежде чем что-либо делать.

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

Однако всякий раз, когда он запускается в цикле do while, все мои функции таймера останавливаются, как если бы они были переведены в спящий режим.

Есть ли другой способ, где я мог бы реализовать это? Если есть, пожалуйста, помогите мне.

Любая помощь приветствуется.

SetTimer(TIMERECHO, 1000, NULL);

BOOL bMoving = FALSE;
do
{
    if (IsMoving(ID, NULL, &bMoving)) 
        GetPosition();
} while (bMoving);


void CLHMDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    switch(nIDEvent)
    {
        case TIMERECHO:
            UpdateData(FALSE);
            m_Seconds++;
            m_TimerEcho.Format(_T("Total time: %ds"), m_Seconds);
            break;
    }
    CDialogEx::OnTimer(nIDEvent);
}

person Ashton    schedule 19.07.2013    source источник


Ответы (3)


MFC имеет цикл обработки сообщений, который получает и отправляет такие сообщения, как WM_TIMER. Если вы выполняете цикл while, этот цикл сообщений не выполняется. Он не спит, он просто ждет, пока вы перестанете грузить процессор.

У вас есть два варианта: (1) Избавиться от длинного цикла и делать то, что он периодически делает с сообщением WM_TIMER. (2) Используйте второй поток для выполнения длительной операции. Это имеет множество трудностей для новичка.

person ScottMcP-MVP    schedule 19.07.2013
comment
Как я могу закодировать его так, чтобы цикл while не загружал процессор ?? Не могли бы вы помочь мне в этом? Благодарю вас - person Ashton; 19.07.2013
comment
Вместо зацикливания используйте таймер для периодического выполнения теста. Каждый раз, когда вы делаете тест, возвращайтесь в MFC. Это способ не грузить процессор. - person ScottMcP-MVP; 19.07.2013
comment
Если вам небезразлично, сколько ЦП использует ваше приложение, не сидите в тесном цикле, проверяя время. Используйте один из таймеров обратного вызова, доступных в Windows (см. ссылку в моем ответе выше) - person MichaelH; 20.07.2013

Это потому, что ваш цикл не обрабатывает сообщения. onTimer вызывается в результате сообщения Windows. Вам нужно продолжать обрабатывать сообщения в цикле или использовать таймер на основе обратного вызова.

Один метод использует CreateTimerQueueTimer http://msdn.microsoft.com/en-us/library/windows/desktop/ms682485%28v=vs.85%29.aspx

person MichaelH    schedule 19.07.2013
comment
Как я могу это сделать? Извините, я новичок в программировании MFC. - person Ashton; 19.07.2013
comment
Таймер на основе обратного вызова по-прежнему нуждается в цикле сообщений - обратный вызов просто вызывается DefWindowProc. - person Roger Rowland; 19.07.2013
comment
Нет, таймер обратного вызова не нуждается в цикле сообщений, если это одна из версий потомков старых таймеров MM, таких как msdn.microsoft.com/en-us/library/windows/desktop/ - person MichaelH; 20.07.2013

Очень простое решение (но не идеальное) для запуска — переместить IsMoving на таймер, как показано ниже:

void CLHMDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
    switch(nIDEvent)
    {
        case TIMERECHO:
           if (IsMoving(ID, NULL, &bMoving)) 
              GetPosition();

            UpdateData(FALSE);
            m_Seconds++;
            m_TimerEcho.Format(_T("Total time: %ds"), m_Seconds);
            break;
    }
    CDialogEx::OnTimer(nIDEvent);
}

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

AfxBeginThread(monitor_motor, this);

UINT monitor_motor(LPVOId lParam)
{
CMyDlg * dlg = (CMyDlg *) lParam;
HWND hWnd = (HWND) dlg->m_hWnd;


do
{
    if (IsMoving(ID, NULL, &dlg->bMoving)) 
        GetPosition();

    Sleep(100); // maybe you don't have to poll too hard and give other hardware chance to communicate?..that's upto you

    PostMessage( hWnd, UPDATE_TIMER_AND_STUFF );
} while (dlg->bMoving);

PostMessage( hWnd, UPDATE_MOTOR_MOVED_COMPLETELY );

}
person zar    schedule 21.07.2013
comment
Извините за задержку с ответом. Но есть ли необходимость называть эту нить в моем измерении? У меня есть OnMeasurementStart. Нужно ли включать ветку в кнопку? - person Ashton; 25.07.2013
comment
Да, это был бы лучший подход. На самом деле я сам занимаюсь чем-то подобным, ресурсов по этой теме не так уж много в отношении использования потоков в таких случаях. - person zar; 25.07.2013
comment
Я имею в виду, как мне вызвать поток в мою кнопку ?? @zadane - person Ashton; 26.07.2013
comment
Не уверен, что именно вы имеете в виду, но используйте AfxBeginThread() для запуска потока при нажатии кнопки - person zar; 26.07.2013
comment
Хорошо, но куда мне включить AfxBeginThread(monitor_motor, this); ?? В заголовочном файле или в файле CPP? @zadane - person Ashton; 29.07.2013
comment
Хорошо, есть еще одна проблема. Когда я вставил все в свой файл CPP, мой ID в IsMoving не определен. Идентификатор - это int в моем заголовочном файле. @zadane - person Ashton; 29.07.2013