CProgressCtrl и поток
От: Аноним  
Дата: 05.07.08 18:04
Оценка:
Тут появилась такая задача.
На диалоге ProgressCtrl и две кнопки, определяющие в какую сторону двигать прогресс.
т.е. по нажатию на кнопку выполняется следующее:

while(m_progress.GetPos() < progress_max)
{
pCtrl->StepIt(); // В ту или иную сторону
// и какой-то код, например
Sleep(100);
}

В цикле пока значение прогресса не дойдет до крайнего значения.
Естественно, что во время выполнения цикла кнопки не доступны. А надо, чтобы во время движения прогресса можно было кнопками менять его направление.
В решении задачи нужно применить многопоточность.
Создал UI поток. И отправляю в него сообщение по нажатию кнопок
pMyThread->PostThreadMessage(WM_USER+1, (WPARAM)m_progress.m_hWnd, (LPARAM)-1/*направление*/);

afx_msg void CMyThread::HandleThreadMessage(WPARAM wParam, LPARAM lParam)
{
    direction = int(lParam);
    CWnd* wnd;
    pCtrl = (CProgressCtrl*)(wnd->FromHandle((HWND)wParam));
    pCtrl->SetStep(direction);
    
    if (direction)
    {
        int min, max;
        pCtrl->GetRange(min, max);
        while((pCtrl->GetPos() >= min)&&(pCtrl->GetPos() <= max))
        {
            pCtrl->StepIt();
            SleepEx(50, false);
        }
    }
}


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

Что-то у меня уже ступор.

Или лучше сделать рабочий поток? И управлять им с помощью событий?

Вообще проще всего без всяких потоков сделать таймер (WM_TIMER) и ни каких проблем, но надо с потоком.
Re: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 04:42
Оценка:
Здравствуйте, Аноним, Вы писали:

А>
А>while(m_progress.GetPos() < progress_max)
А>{
А>pCtrl->StepIt(); // В ту или иную сторону
А>// и какой-то код, например
А>Sleep(100);
А>}
А>


Вынеси этот цикл в рабочий поток.
Re[2]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 04:51
Оценка: 1 (1)
Здравствуйте, dandy, Вы писали:

D>Вынеси этот цикл в рабочий поток.


В потоке придется сделать синхронизацию, например перед запуском нового потока убивать старый, если он запущен и т.п. И в качестве прарметра обычно выгоднее всего передавать указатель на оконный класс, в котором находятся нужные тебе контролы.
Re[3]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 06:40
Оценка:
Здравствуйте, dandy, Вы писали:

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


Спасибо, уже сделал через рабочий поток.
Но тут возникла проблема.
В потоке я жду двух событий: по одному (ManualReset) я двигаю прогресс, а по второму выхожу из потока (AfxEndThread(0); )
А в основном потоке при выходе делаю:

    ProgressEvent.ResetEvent();
    ExitEvent.SetEvent();
    WaitForSingleObject(pThread->m_hThread, INFINITE);


И тут все повисает. Насколько я понял взаимоблокировка. Как этого избежать?
Re[4]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 07:41
Оценка:
Здравствуйте, KokS, Вы писали:

KS>В потоке я жду двух событий: по одному (ManualReset) я двигаю прогресс, а по второму выхожу из потока (AfxEndThread(0); )

KS>А в основном потоке при выходе делаю:

В этом случае синхронизировать лучше примерно так:
Перед созданием нового потока проверять, не работает ли уже предыдущий, проще всего по указателю на объект CWinThread, который делаешь членом класса и инициализируешь нулем в конструкторе, при создании потока присваиваешь ему возврат функции AfxBeginThread. Если указатель не нулевой, событие, по которому выходишь из потока, устанавливаешь в сигнальное состояние, какое — то время ждешь установки указателя в 0 функцией потока, выходишь из функции потока при установленном событии, при выходе из потока устанавливаешь указатель в 0. Можно еще кой — чего добавить, но для этого случая должно быть достаточно.
Re[5]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 07:51
Оценка:
Здравствуйте, dandy, Вы писали:

D>В этом случае синхронизировать лучше примерно так:

D>Перед созданием нового потока проверять, не работает ли уже предыдущий, проще всего по указателю на объект CWinThread, который делаешь членом класса и инициализируешь нулем в конструкторе, при создании потока присваиваешь ему возврат функции AfxBeginThread. Если указатель не нулевой, событие, по которому выходишь из потока, устанавливаешь в сигнальное состояние, какое — то время ждешь установки указателя в 0 функцией потока, выходишь из функции потока при установленном событии, при выходе из потока устанавливаешь указатель в 0. Можно еще кой — чего добавить, но для этого случая должно быть достаточно.

Хочется понять, почему не работает мой вариант.
И как ждать установки указателя в 0? В цикле? Не красиво.
Re[6]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 08:06
Оценка:
Здравствуйте, KokS, Вы писали:

KS>Здравствуйте, dandy, Вы писали:


KS>Хочется понять, почему не работает мой вариант.

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

KS>И как ждать установки указателя в 0? В цикле? Не красиво.

Напиши Sleep(сколько_нужно) один раз. Некрасиво — да. Красиво будешь писать тогда, когда сможешь писать и красиво и правильно.
Re[7]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 08:28
Оценка:
Здравствуйте, dandy, Вы писали:

D>Здравствуйте, KokS, Вы писали:


KS>>Здравствуйте, dandy, Вы писали:


KS>>Хочется понять, почему не работает мой вариант.

D>Судя по всему, ты запускаешь поток при создании диалога. Запускай по нажатию кнопки, с предварительной проверкой и последующим убийством уже запущенного потока — если он уже запущен, конечно.

На создание/завершение потока тратиться много процессорного времени, поэтому часто (как в моем случае) этого лучше не делать. Мне как раз и надо, чтобы поток создавался при создании диалога и при закрытии завершался.

KS>>И как ждать установки указателя в 0? В цикле? Не красиво.

D>Напиши Sleep(сколько_нужно) один раз. Некрасиво — да. Красиво будешь писать тогда, когда сможешь писать и красиво и правильно.

Так ведь я хочу научиться писать красиво и правильно. Потому и спрашиваю
Re[8]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 08:32
Оценка:
Здравствуйте, KokS, Вы писали:

KS>На создание/завершение потока тратиться много процессорного времени, поэтому часто (как в моем случае) этого лучше не делать. Мне как раз и надо, чтобы поток создавался при создании диалога и при закрытии завершался.

При обработке нажатия на кнопку это время пренебрежимо мало.

KS>Так ведь я хочу научиться писать красиво и правильно. Потому и спрашиваю

Тогда смотри WaitForSingleObject на хендл убиваемого потока.
Re[9]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 08:46
Оценка:
D>Здравствуйте, KokS, Вы писали:

Все написанное мной верно, если диалог показывается из процесса.
Если из dll, то все сложнее.
Re[9]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 08:51
Оценка:
Здравствуйте, dandy, Вы писали:

D>Тогда смотри WaitForSingleObject на хендл убиваемого потока.


Я и делаю
ProgressEvent.ResetEvent();
ExitEvent.SetEvent();
WaitForSingleObject(pThread->m_hThread, INFINITE);

как я уже писал.

Но так все виснет. Как будто ExitEvent не выставляется.

В рабочем потоке:

while (1)
    {
        dwEvent = WaitForMultipleObjects(2, pst->hEvents, FALSE, INFINITE);
        switch(dwEvent)
        {
        case WAIT_OBJECT_0 + 0:
                        // код
            break;
        case WAIT_OBJECT_0 + 1: // ExitEvent
            AfxEndThread(0);
            break;
        }
    }
Re[10]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 08:52
Оценка:
Здравствуйте, dandy, Вы писали:

D>Все написанное мной верно, если диалог показывается из процесса.

D>Если из dll, то все сложнее.

из процесса
Re[10]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 09:15
Оценка:
Здравствуйте, KokS, Вы писали:

Твой код работать не должен.


//В потоке:
if(WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
{
    pDlg->pThread_ = 0;
    return 0;
}

//В обработчике:
if(this->pThread_)
{
if(WaitForSingleObject(pThread->m_hThread, сколько_надо) != WAIT_OBJECT_0) 
{
     AfxMessageBox(m_hWnd, _T("Failed to kill thread normally, there thill be a memory leaks"));
     TerminateThread(this->pThread_->m_hThread)
     this->pThread_ = 0;
}
}


примерно так.
Re[11]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 09:18
Оценка:
D>//В обработчике:
D>if(this->pThread_)
D>{
// Забыл написать: тут нужно выставить событие hEvent в сигнальное состояние
D>if(WaitForSingleObject(pThread->m_hThread, сколько_надо) != WAIT_OBJECT_0)
D>{
D> AfxMessageBox(m_hWnd, _T("Failed to kill thread normally, there will be a memory leaks"));
D> TerminateThread(this->pThread_->m_hThread)
this->>pThread_ = 0;
D>}
D>}
Re[11]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 09:22
Оценка:
Здравствуйте, dandy, Вы писали:

D>Твой код работать не должен.


Обьясните, пожалуйста, почему. Я просто хочу понять...
Re[12]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 09:29
Оценка:
Здравствуйте, KokS, Вы писали:

KS>Обьясните, пожалуйста, почему. Я просто хочу понять...


Насколько понял, в потоке дорабатывает до этой строки:
dwEvent = WaitForMultipleObjects(2, pst->hEvents, FALSE, INFINITE);
и, как там и написано, останавливается и бесконечно долго ждет. Если непонятно, воспользуйся дебаггером.
Re[13]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 09:36
Оценка:
Здравствуйте, dandy, Вы писали:

D>Здравствуйте, KokS, Вы писали:


KS>>Обьясните, пожалуйста, почему. Я просто хочу понять...


D>Насколько понял, в потоке дорабатывает до этой строки:

D>dwEvent = WaitForMultipleObjects(2, pst->hEvents, FALSE, INFINITE);
D>и, как там и написано, останавливается и бесконечно долго ждет. Если непонятно, воспользуйся дебаггером.

Но я же выставляю событие ExitEvent (оно же pst->hEvents[1])
По нему поток и должен завершаться.
Re[14]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 09:44
Оценка:
Здравствуйте, KokS, Вы писали:

KS>Но я же выставляю событие ExitEvent (оно же pst->hEvents[1])

KS>По нему поток и должен завершаться.

Могу только предположить, что ты не делаешь ему ResetEvent()
Если не так, тогда выкладывай уже весь код.
Re[15]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 09:48
Оценка:
Здравствуйте, dandy, Вы писали:

И INFINITE ждать не нужно, так делается только в примерах MSDN.
Re[16]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 10:48
Оценка:
// *.h
    CWinThread* pThread;
    static UINT ThreadProc( LPVOID pParam );
    struct toThread 
    {
        HANDLE hEvents[2];
        HWND hProgress;
    } st;
    CEvent ProgressEvent;
    CEvent ExitEvent;

//*.cpp
// инициализация событий:
:ProgressEvent(FALSE, TRUE), ExitEvent(FALSE, TRUE)

//OnInitDialog
    st.hEvents[0] = ProgressEvent.m_hObject;
    st.hEvents[1] = ExitEvent.m_hObject;
    st.hProgress = m_progress.m_hWnd;
    pThread = AfxBeginThread(ThreadProc, &st, 0, 0, CREATE_SUSPENDED, NULL); 
    pThread->m_bAutoDelete = FALSE;
    pThread->ResumeThread();

// Функция рабочего потока
UINT C_Dlg::ThreadProc(LPVOID pParam)
{
    toThread* pst = (toThread*)pParam;
    CWnd* wnd;
    CProgressCtrl* pCtrl = (CProgressCtrl*)(wnd->FromHandle(pst->hProgress));

    DWORD dwEvent;
    while (1)
    {
        dwEvent = WaitForMultipleObjects(2, pst->hEvents, FALSE, INFINITE);
        TRACE(_T("dwEvent = %d\n"), dwEvent);
        switch(dwEvent)
        {
        case WAIT_OBJECT_0 + 0:
                        // Код, который должен выполняться циклически, пока ProgressEvent установлен, поэтому сдесь я его не сбрасываю
            break;
        case WAIT_OBJECT_0 + 1:
            ResetEvent(pst->hEvents[1]);
            AfxEndThread( 0, FALSE );
            break;
        }
    }
    return 0;
}

// Функция выхода:
void C_Dlg::Exit()
{
    ProgressEvent.ResetEvent();
    ExitEvent.SetEvent();  // Выставляю событие завершения потока, но поток его не получает  :xz: 
    WaitForSingleObject(pThread->m_hThread, 2000);  // Жду когда поток завершиться, но т.к. поток не получил ExitEvent этого не происходит
    delete pThread;
}
//
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.