Проблема в приостановке 2 потоков одновременно в MFC! - PullRequest
1 голос
/ 29 апреля 2010

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

Thread1 - для обновления индикатора выполнения (в соответствии с циклом) статического текста, который будет отображать количество (счетчик цикла). Thread2 - обновить другой статический текст, который будет просто отображать имя

В основном, если пользователь нажимает кнопку «Пуск», индикатор выполнения активируется, и в то же время количество файлов и имя отображаются параллельно. Есть еще одна операция, при которой, если пользователь нажимает кнопку pause, он (поток) должен приостановиться, пока пользователь не нажмет кнопку «Продолжить». Проблема в том, что вышеупомянутое не будет работать (не приостанавливать и возобновлять) для обоих потоков .., но работает для одного потока. Пожалуйста, проверьте код, чтобы получить идею, и ответьте мне, что можно сделать!

при нажатии кнопки запуска

void CThreadingEx3Dlg::OnBnClickedStart()
{
    m_ProgressBar.SetRange(start,end);
    myThread1 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction1,this);
    myThread2 = AfxBeginThread((AFX_THREADPROC)MyThreadFunction2,this);
}

thread1

UINT MyThreadFunction1(LPARAM lparam)
{
    CThreadingEx3Dlg* pthis = (CThreadingEx3Dlg*)lparam;
    for(int intvalue =pthis->start;intvalue<=pthis->end; ++intvalue)
    {
        pthis->SendMessage(WM_MY_THREAD_MESSAGE1,intvalue);
    }
    return 0;
}

функция thread1

LRESULT CThreadingEx3Dlg::OnThreadMessage1(WPARAM wparam,LPARAM lparam)
{
    int nProgress= (int)wparam;
     m_ProgressBar.SetPos(nProgress);
       CString strStatus;
    strStatus.Format(L"Thread1:Processing item: %d", nProgress);
        m_Static.SetWindowText(strStatus);
    Sleep(100);
    return 0;
}

резьба2

UINT MyThreadFunction2(LPARAM  lparam)
{
    CThreadingEx3Dlg* pthis = (CThreadingEx3Dlg*)lparam;
    for(int i =pthis->start;i<=pthis->end;i++)
    {
        pthis->SendMessage(WM_MY_THREAD_MESSAGE2,i);
    }
    return 0;
}

функция thread2

LRESULT CThreadingEx3Dlg::OnThreadMessage2(WPARAM wparam,LPARAM lparam)
{
        m_Static1.GetDlgItem(IDC_STATIC6);
        m_Static1.SetWindowTextW(L"Thread2 Running");
        Sleep(100);
        m_Static1.SetWindowTextW(L"");
        Sleep(100);
        return TRUE;

}

void CThreadingEx3Dlg::OnBnClickedPause()
{
    // TODO: Add your control notification handler code here
    if(!m_Track)
    {
        m_Track = TRUE;
        GetDlgItem(IDCANCEL)->SetWindowTextW(L"Resume");
        myThread1->SuspendThread();
        WaitForSingleObject(myThread1->m_hThread,INFINITE);
        myThread2->SuspendThread();
        m_Static.SetWindowTextW(L"Paused..");

    }
    else
    {
        m_Track = FALSE;
        GetDlgItem(IDCANCEL)->SetWindowTextW(L"Pause");
        myThread1->ResumeThread();
        myThread2->ResumeThread();

        /*myEventHandler.SetEvent();
        WaitForSingleObject(myThread1->m_hThread,INFINITE);*/
    }
}

Ответы [ 3 ]

4 голосов
/ 29 апреля 2010

Я подумал, что я должен обобщить некоторые обсуждения в комментариях в ответ.

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

Это требование подробно описано в статье Джо Ньюкомера о рабочих потоках (см. «Рабочие потоки и графический интерфейс II: не прикасайтесь к графическому интерфейсу»).

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

Альтернатива - использовать PostMessage. Это приводит к тому, что сообщение, добавляемое в очередь, обрабатывается в цикле сообщений основного потока. Когда основной поток запускается, он обрабатывает сообщения в порядке их добавления в очередь, вызывая сами обработчики сообщений. Поскольку основной поток владеет окнами, безопасно обновлять элементы управления.

Вам также следует помнить, что SuspendThread и ResumeThread сложно сделать правильно. Возможно, вы захотите прочитать этот раздел статьи Джо Ньюкомера, в которой описаны некоторые опасности.


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

BEGIN_MESSAGE_MAP(CThreadingEx3Dlg, CDialog)
   ON_WM_DESTROY()
   ON_WM_TIMER()
END_MESSAGE_MAP()

void CThreadingEx3Dlg::OnTimer(UINT_PTR nTimerID)
{
   static int progress = 0;
   if (nTimerID == 1)
   {
      m_ProgressBar.SetPos(progress);
      CString strStatus;
      strStatus.Format(_T("Processing item: %d"), progress);
      m_Static.SetWindowText(strStatus);
      progress++;
      if (progress > end) // If we've reached the end of the updates.
         KillTimer(1);
   }
}

BOOL CThreadingEx3Dlg::OnInitDialog()
{
   // ... initialize controls, etc, as necessary.
   SetTimer(1, 100, 0);
}

void CThreadingEx3Dlg::OnDestroy()
{
   KillTimer(1);
}

Если вы хотите, чтобы оба обновления обрабатывались одновременно, они могут использовать один и тот же таймер. Если они должны происходить в разное время (например, один с интервалом в 100 мс, а другой с интервалом в 150 мс), вы можете дважды вызвать SetTimer с разными идентификаторами. Чтобы приостановить действие, позвоните KillTimer. Для возобновления звоните снова SetTimer.

1 голос
/ 29 апреля 2010

Многопоточность и очереди сообщений - довольно сложная игра. Когда вы отправляете сообщение из ThreadA в тот же поток, он просто вызывает обработчик сообщения. Если вы делаете это из ThreadA в другой поток (ThreadB), тогда это становится более сложным. Затем ThreadA отправляет сообщение в очередь сообщений ThreadB и ожидает сигнала, чтобы сообщить, что ThreadB завершил обработку сообщения и отправил возвращаемое значение. Это поднимает мгновенную проблему. Если ThreadB не перекачивает сообщения, то у вас тупик, так как сообщение в ThreadB никогда не будет «отправлено». Это также поднимает еще большую проблему. Если сообщение ThreadB должно отправить сообщение элементу управления, созданному в ThreadA, у вас возникла серьезная архитектурная проблема. Поскольку ThreadA в настоящее время приостановлено в ожидании возврата ThreadB, а ThreadB приостановлено в ожидании возврата ThreadA. Ничего не случится ... Они оба будут сидеть в подвешенном состоянии.

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

В целом, хотя ваши потоки довольно бессмысленны, потому что вы сразу же отправляете сообщение в основной поток, чтобы выполнить некоторую обработку. Зачем вообще начинать темы? Вы также можете не беспокоиться, потому что потоки будут просто сидеть и ждать, пока основной поток вернется.

Почему вы все равно "WaitForSingleObject", когда вы приостанавливаете первый поток? Почему бы просто не приостановить их обоих.

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

0 голосов
/ 29 апреля 2010

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

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