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;
}
//
Re[17]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 11:49
Оценка: 2 (1)
Здравствуйте, KokS, Вы писали:


class CManualEvent : public CEvent
{
public:
    CManualEvent() : CEvent(FALSE, TRUE) {}
};

//В классе диалога:
    static UINT ThreadProc( LPVOID pParam );
    CManualEvent ProgressEvent;
    CManualEvent ExitEvent;

// в OnInit...
    ProgressEvent.ResetEvent();
    ExitEvent.ResetEvent();
    pThread = AfxBeginThread(ThreadProc, this); 

UINT CTestKocsDlg::ThreadProc(LPVOID pParam)
{
    CTestKocsDlg * p = static_cast<CTestKocsDlg *>(pParam);

    while(1)
    {

        if(WAIT_OBJECT_0 == WaitForSingleObject(p->ProgressEvent, 200))
        {
            return 0;
        }

        if(WAIT_OBJECT_0 == WaitForSingleObject(p->ExitEvent, 0))
        {
            return 0;
        }

        // Тут делай, что нужно

    }
    return 0;
}

void CTestKocsDlg::OnDestroy() 
{
    ExitEvent.ResetEvent();
    ExitEvent.SetEvent();
    if(::WaitForSingleObject(pThread->m_hThread, 400) != WAIT_OBJECT_0)
    {
        AfxMessageBox(_T("Oops!"));
        ::TerminateThread(pThread->m_hThread, 0);
    }
    CDialog::OnDestroy();

}


Насчет WaitForMultipleObjects — не пользовал
Re[18]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 11:56
Оценка:
Здравствуйте, dandy, Вы писали:

D>Насчет WaitForMultipleObjects — не пользовал


За ответ спасибо.
Но все равно хочется разобраться с WaitForMultipleObjects.
Что у меня не так? Может еще кто-нибудь подскажет?
Re[18]: CProgressCtrl и поток
От: KokS  
Дата: 06.07.08 13:00
Оценка:
Здравствуйте, dandy, Вы писали:

D>[ccode]

D>class CManualEvent : public CEvent
.....................


В вашем варианте этот самый Oooops тоже иногда выскакивает
Re[19]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 13:51
Оценка:
Здравствуйте, KokS, Вы писали:

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


KS>В вашем варианте этот самый Oooops тоже иногда выскакивает


Нужно увеличить продолжительность ожидания завершения потока в OnDestroy()
Re[20]: CProgressCtrl и поток
От: dandy  
Дата: 06.07.08 13:57
Оценка:
Здравствуйте, dandy, Вы писали:

D>Нужно увеличить продолжительность ожидания завершения потока в OnDestroy()

С другой стороны, если ты не модифицировал код, то он выскакивать не должен.
Если модифицировал, то см. предыдущий коммент.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.