Проблема с интерфейсом в многопоточном приложении с событиями

Общие вопросы, не зависящие от языка реализации.

Модераторы: Duncon, Hawk, Romeo, Eugie

engine
Сообщения: 13
Зарегистрирован: 10 фев 2008, 17:50

В приложении 3 потока. Первый основной, в нем находится оконная процедура обработки сообщений WndProc, создаются два глобальных события (hEvent1 и hEvent2) и два потока (Thread1 и Thread2). Каждому событию hEventx соответствует свой поток Threadx. Структура потоков одинакова и заключается в следующем. В потоке находится бесконечный цикл в котором вызывается ф-ия WaitForSingleObject(hEventx, INFINITE), на которой поток должен остановиться и продолжить свою работу после освобождения события. Освобождения события(SetEvent(hEventx)) происходит в основном потоке в функции WndProc при появлении сообщения о нажатии клавиши Enter на клавиатуре. При нажатии этой клавиши оба события освобождаются и потоки продолжают свою работу. Но проблема в том, что когда потоки переходят в режим ожидания события (натыкаются на WaitForSingleObject), интерфейс окна повисает. И функция WndProc не реагирует на нажатие клавиш. Хотя вызов WaitForSingleObject происходит исключительно в двух созданных потоках.
Примерная структура программы следующая:

Код: Выделить всё

WndProc(...)
{
   switch (Message)
   {
       case WM_CREATE:
       {
           hEvent1=CreateEvent(NULL,false,false,NULL);
           hEvent2=CreateEvent(NULL,false,false,NULL);
           _beginthread(Thread1,...);
           _beginthread(Thread2,...);
       }
       break;
       case WM_KEYDOWN:
       {
           SetEven(hEvent1);
           SetEven(hEvent2);
       }
       break;
       ...
   }

}
Thread1(...)
{
   ...
   while(1)
   {
       WaitForSingleObject(hEvent1, INFINITE);
       ...
   }
   ...
}
Thread2(...)
{
   ...
   while(1)
   {
       WaitForSingleObject(hEvent2, INFINITE);
       ...
   }
   ...
}
Аватара пользователя
WinMain
Сообщения: 929
Зарегистрирован: 14 янв 2005, 10:30
Откуда: Москва
Контактная информация:

Поскольку ты выводишь объекты Event из режима ожидания, то циклы

Код: Выделить всё

   while(1)
   {
       ...
   }
просто "вешают" процессор и он работает только на выполнение этих циклов.
engine
Сообщения: 13
Зарегистрирован: 10 фев 2008, 17:50

На следующей итерации цикла поток опять входит в режим ожидания на ф-ии: WaitForSingleObject(hEvent1, INFINITE), так как событие hEvent становится занятым автоматически после WaitForSingleObject. Я это указал при создании события, т.е. второй аргумент ф-ии CreateEvent bManualReset=false. Поэтому никаких зацикливаний быть не может.Сколько раз я нажму клавишу, столько и будет итераций циклов.
ssDev
Сообщения: 50
Зарегистрирован: 20 янв 2005, 14:41

У тебя ошибка гдето в другом месте
Я для интереса запустил этот код и он работает (за исключением SetEven)
Hawk
Сообщения: 216
Зарегистрирован: 17 фев 2004, 14:52
Откуда: СПб
Контактная информация:

Не делают ли потоки какой-нить SendMessage основному окну?
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

После WaitForSingleObject нужно делать вручную прокачку сообщений по той причине, что цикл while (1) крутится бесконечно и до главного цикла обработки сообщение исполнение не доходит. Вот функция, опробованная веками:

Код: Выделить всё

inline BOOL WaitWithDlgMessageLoop(HWND hDlg, HANDLE hEvent)
{
	DWORD dwRet;
	MSG msg;

	while (1)
	{
		dwRet = ::MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);

		if (dwRet == WAIT_OBJECT_0)
		{
			return TRUE;    // The event was signaled
		}

		if (dwRet != WAIT_OBJECT_0 + 1)
		{
			break;          // Something else happened
		}

		// There is one or more window message available. Dispatch them
		while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			// check for unicode window so we call the appropriate functions
			if (msg.hwnd != NULL && ::IsWindowUnicode(msg.hwnd))
			{
				if (::GetMessageW(&msg, NULL, 0, 0) > 0)
				{
					if (!::IsDialogMessageW(hDlg, &msg))
					{
						::TranslateMessage(&msg);
						: :D ispatchMessageW(&msg);
					}
				}
			}
			else
			{
				if (::GetMessageA(&msg, NULL, 0, 0) > 0)
				{
					if (!::IsDialogMessageA(hDlg, &msg))
					{
						::TranslateMessage(&msg);
						: :D ispatchMessageA(&msg);
					}
				}
			}

			if (::WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
			{
				return TRUE; // Event is now signaled.
			}
		}
	}

	return FALSE;
}
Вызывай её вместо цикла while (1).
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Hawk
Сообщения: 216
Зарегистрирован: 17 фев 2004, 14:52
Откуда: СПб
Контактная информация:

Romeo - то что ты написал актуально если Wait в главном потоке, а у Engine Wait в других потоках, и главный должен прекрасно крутиться пока остальные потоки ждут hEvent в своих Wait-ах. И на цикл обработки сообщений эти потоки никак не влияют. Тут где-то в другом месте проблема. Как я понял Wait-ы почему-то не ждут события а сразу проваливаются.
engine
Сообщения: 13
Зарегистрирован: 10 фев 2008, 17:50

И я заметил интересное поведение окна диалога:
оказывается интерфейс зависает не сразу после перехода потоков на WaitForSingleObject, а после того как поступит сообщение главной оконной процедуре. Потому что, после перехода потоков на WaitForSingleObject курсор в рич эдите мигает и можно вводить в него слова(но после нажатия Enter они не исчезают, это значит сообщение от рич эдита не обрабатываются оконноц процедурой) и даже выделяются отдельные элементы в лист боксе, но стоит только кликнуть на самом окне диалога вне контролов как все зависает.
Но определенно сказать, что оконная процедура зависает нельзя, она еще принимает сообщения(только не от дочерних окон), даже после того, когда интерфейс не реагирует. Я это выяснил проведя опыт:
Выполнив все действия после которых оба потока перешли в ожидание на ф-ии WaitForSingleObject, я кликнул мышкой на окне диалога и оно зависло. После этого я установил брекпоинт на самое начало оконной процедуры. Затем я накрыл диалоговое окно другим окном(не имеет значения каким) и убрал это окно. По логике нашему приложению должно поступить сообщение о перерисовки диалогового окна. Так и случилось - брекпоинт сработал, значит сообщения от системы поступают и обрабатываются. Но диалоговое окно перерисовывается, только без контролов(простое серое окошко), вот на рисунке часть окна я закрыл другим окном:
http://forum.codenet.ru/attachment.php? ... 1203326491
Можно сделать вывод: обмен сообщениями между главным окном и его дочерними окнами не осуществляется, а обмен сообщениями между системой и главным окном работает нормально.
engine
Сообщения: 13
Зарегистрирован: 10 фев 2008, 17:50

Более детальное описание программы приведено здесь: http://forum.codenet.ru/showthread.php? ... post234471
ssDev
Сообщения: 50
Зарегистрирован: 20 янв 2005, 14:41

Хмм.....
У тебя передаются сообщения к окну из другого потока. Попробуй убрать вызов Scanf из Thread и я не удевлюсь если у тебя исчезнут зависания
Ответить