Форум: "WinAPI";
Текущий архив: 2006.06.25;
Скачать: [xml.tar.bz2];
ВнизВопросы по обмену сообщениями между TThread ами Найти похожие ветки
← →
Lucefer (2006-03-23 23:41) [0]Итак, у меня есть несколько парно работающих потоков, один выполняет приём данных из сокета, другой будет работать с базой данных (при создании запоминает подключение, параметры и т.д. а дальше должен обабатывать сообщения и выполнять операции над б.д.).
Т.е. один из потоков должен отыскать своего "напраника" и послать ему некое сообщение. Пытаюсь разобраться как это можно сделать. Для общения между потоками есть функция
PostThreadMessage.
Уточнение 1: Если я создаю ttr:TMyThread то в качестве DWORD idThread должен выступать ttr.Handle?
т.е. команда будет выглядеть так: PostThreadMessage(ttr.Handle, WM_USER+100, 0, 0);
Уточнение 2: В справке к функции PostThreadMessage на родном английском ;) языке написано:
<quote>
The function fails if the specified thread does not have a message queue. The system creates a thread"s message queue when the thread makes its first call to one of the Win32 USER or GDI functions.</quote>
Далее по тексту: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/messagesandmessagequeues/messagesandmessagequeuesreference/messagesandmessagequeuesfunctions/postthreadmessage.asp
Означает ли это что я должен во втором потоке сделать нечто, что создаст очередь сообщений. Т.е. по тексту вызвать PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE) либо в том потоке в котором хочу отправить сообщение отправлять его в цикле until PostThreadMessage succeeds?
Например так:
while PostThreadMessage(ttr.Handle, WM_USER+100, 0, 0)=0
do sleep(10);
продолжение вопросов следует.
← →
Lucefer (2006-03-23 23:57) [1]т.е.
while (PostThreadMessage(ttr.Handle, WM_USER+100, 0, 0)=null)
do sleep(10);
как я понял при первом вызове PostThreadMessage сообщение теряется(?) но для того потока создаётся очередь сообщений? так что-ли?
Теперь про поток, который будет обрабтывать сообщения.
Я создаю функции обработчики сообщений, как мне подсказывают, по шаблонуprocedure SomeServiceMessageHandler(var Msg: TMessage); message WM_USER+XX;
На Execute создаю цикл выборки сообщений. Как один из вариантов это может быть (как мне подсказывают)
while (not Terminated) and GetMessage(Msg, 0, 0, 0) do begin
Dispatch(Msg.Message);
end;
Сразу ламерский вопрос. В параметрах фильтра минимума и максимума 0,0 - указывает на то что надо принимать все сообщения?
Уточнение: Надо бы как то анализировать возвращаемый GetMessage результат, так по крайней мере советуют в MSDN.
Главное уточнение: что произойдёт если мне в этот поток не будет направлено ни одного сообщения и надо будет завершить поток по Terminated?
← →
Lucefer (2006-03-24 00:37) [2]С PostThreadMessage разобрался - там надо писать
PostThreadMessage(ttr.ThreadID, WM_USER+100, 0, 0)
а не
PostThreadMessage(ttr.Handle, WM_USER+100, 0, 0);
Сообщения передаются, потоком ловятся, висюков нет, но терминэйт ес-но не отрабатывается. теперь вместо него надо WM_CLOSE передавать :(
← →
Eraser © (2006-03-24 01:26) [3]
> Lucefer
перечитай ветку http://delphimaster.net/view/4-1142453912/
желательно несколько раз.
2 примера построения цикла приёма сообщений там точно есть!
> Уточнение 1: Если я создаю ttr:TMyThread то в качестве DWORD
> idThread должен выступать ttr.Handle?
> т.е. команда будет выглядеть так: PostThreadMessage(ttr.
> Handle, WM_USER+100, 0, 0);
ThreadID и ThreadHandle это совершенно разные понятия! Ф-я PostThreadMessage требует ThreadID.
> postthreadmessage.asp
> Означает ли это что я должен во втором потоке сделать нечто,
> что создаст очередь сообщений. Т.е. по тексту вызвать PeekMessage(&msg,
> NULL, WM_USER, WM_USER, PM_NOREMOVE) либо в том потоке
> в котором хочу отправить сообщение отправлять его в цикле
> until PostThreadMessage succeeds?
> Например так:
>
> while PostThreadMessage(ttr.Handle, WM_USER+100, 0, 0)=0
> do sleep(10);
эт вообще не понятно что! где откопал такое то? )
PS Добрый совет. Почитай книгу Рихтера "Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows." - не пожалеешь и глупые вопросы сразу исчезнут.
скачать тут можно http://rouse.front.ru/
← →
Сергей М. © (2006-03-24 08:09) [4]
> как я понял при первом вызове PostThreadMessage сообщение
> теряется(?)
Теряется в случае если целевой поток на этот момент еще не создал свою очередь сообщений. А создается она при первом же вызове в целевом потоке одной из ф-ций, обращающихся к очереди (GetMessage, PeekMessage, WaitMessage)
Отсюда следует, что если существует вероятность ситуации, когда поток-передатчик начнет отправку сообщения раньше чем поток-приемник создаст очередь, то одним из решений м.б. организация в потоке-передатчике "тестирующего" цикла:
while not PostThreadMessage(TargetThreadId, WM_USER, 0, 0) do sleep(..);
Цикл будет крутиться до момента пока целевая очередь не будет создана.
← →
Lucefer (2006-03-24 09:44) [5]Спасибо за участие. Наклепал тестовое приложение поэкспериментировал и разобрался что к чему.
Т.е. получилось:
mw : HWND; //результат от AllocateHWnd(FakeWndProc) в "родительском" потоке
protected
procedure SomeServiceMessageHandler(var Msg: TMessage); message WM_USER+100;
procedure Execute; override;
procedure TTestThread.SomeServiceMessageHandler(var Msg: TMessage);
begin
//Тестовая отправка в осн. поток.
PostMessage(mw, WM_USER + 4, 0, 0);
WriteToFile("Query from main thread!");
end;
procedure TTestThread.Execute;
var
Msg: TMsg;
begin
PeekMessage(Msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
PostMessage(mw, WM_USER + 2, 0, 0); //Отправка сообщения в осн. поток о том что запущен процесс.
WriteToFile("Thread activated!"); //Тестовая запись в файл
while (not Terminated) and GetMessage(Msg, 0, 0, 0) do begin
Dispatch(Msg.Message);
end;
end;
Единственное, немного необычный получился алгоритм уничтожения этого потока:
if Assigned(ttr) then begin \\ttr:TTestThread;
if not ttr.Suspended then begin
ttr.Terminate;
while true do begin
if PostThreadMessage(ttr.ThreadID, WM_CLOSE, 0, 0) then break;
//i:=GetLastError();
sleep(10);
end;
ttr.WaitFor;
end;
ttr.Free;
ttr:=nil;
end;
Поток естественно FreeOnTerminate = FALSE;
← →
Leonid Troyanovsky © (2006-03-24 10:06) [6]
> Lucefer (24.03.06 09:44) [5]
> Единственное, немного необычный получился алгоритм уничтожения
> этого потока:
>
> if Assigned(ttr) then begin \\ttr:TTestThread;
> if not ttr.Suspended then begin
> ttr.Terminate;
> while true do begin
> if PostThreadMessage(ttr.ThreadID, WM_CLOSE, 0, 0)
> then break;
Это не совсем то. Сообщение WM_CLOSE шлют окну (sent).
В данном же случае, потоку нужно послать WM_QUIT,
для выхода из message loop.
Т.е., Terminate; override;
inherited;
PostThreadMessage(ThreadId, WM_QUIT, 0, 0)
Придется тоже самое cделать в Destroy, т.к. Terminate не виртуальный.
Кста, в таком случае не нужен while (not Terminated).
Можно посмотреть на реализацию
http://www.rsdn.ru/Forum/Message.aspx?mid=883505&only=1
--
Regards, LVT.
← →
Сергей М. © (2006-03-24 10:15) [7]
> немного необычный получился алгоритм уничтожения этого потока
Это можно сделать проще и изящней :
TTestThread = class(TThread)
private
procedure MsqTerminateThread(var Message: TMessage); message WM_CLOSE;
...
procedure TTestThread.MsqTerminateThread(var Message: TMessage);
begin
Terminate;
end;
..
destructor TTestThread.Destroy;
begin
if GetCurrentThreadId <> ThreadId then //на случае если вдруг понадобится FreeOnTerminate = True
begin
PostThreadMessage(ThreadId, WM_CLOSE, 0, 0);
if Suspended then Resume;
end;
inherited;
end;
...
//уничтожаем поток обычным образом
ttr.Free;
← →
Сергей М. © (2006-03-24 10:18) [8]Ну или как советует Leonid Troyanovsky © (24.03.06 10:06) [6] (что еще проще):
destructor TTestThread.Destroy;
begin
if GetCurrentThreadId <> ThreadId then
begin
PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
if Suspended then Resume;
end;
inherited;
end;
...
procedure TTestThread.Execute;
var
Msg: TMsg;
begin
...
while GetMessage(Msg, 0, 0, 0) do
Dispatch(Msg.Message);
..
end;
← →
Сергей М. © (2006-03-24 11:26) [9]
> один из потоков должен отыскать своего "напраника"
Как ты это реализовал в дан.случае ? Какой способ предпочел ?
Просто интересно..
← →
Slym © (2006-03-24 15:29) [10]Синхронизация потоков сообшениями? Дурдом!
Евенты, Семафоры, мютексы, и кретические секции вам на что?
TТранспортныйПоток=class(TThread)
Event:TEvent;
procedure Execute;
TПотокОбработки=class(TThread)
procedure Execute;
TТранспортныйПоток.Execute;
begin
while not terminated do
begin
Прием;
if ПринятаПачка then
Event.PulseEvent;
end;
end;
TПотокОбработки.Execute;
begin
while not terminated do
begin
if ТранспортныйПоток.Event.WaitFor=Signaled then
Обработка;
end;
end;
← →
Slym © (2006-03-24 15:31) [11]Соединять потоки нужно на этапе создания, а не > должен отыскать своего "напраника"
← →
Slym © (2006-03-24 15:39) [12]Врочитав "первородную" верку вкурил, надежнее:
TSomeService = class(TService)
private
CriticalSection:TCriticalSection;
public
procedure ThreadReport(ReportType:integer;const ReportMsg:string);
end;
TSomeService.ThreadReport(ReportType:integer;const ReportMsg:string);
begin
CriticalSection.Enter;
try
case ReportType of
CM_CLIENT_IS_ACTIVE: begin
...
end;
CM_CLIENT_ADDCOMMAND: begin
...
end;
end;
finally
CriticalSection.Leave;
end;
end;
← →
Lucefer (2006-03-24 23:00) [13]1.
В прниципе у созданного треда нет ни окна ни какой бы то ни было оконной функции, и по какому сообщению выход делать значения, на мой взгляд не имеет, можно хоть на WM_PAINT :-) Хотя WM_QUIT - меньше путаницы вносит. Я прав?
2. Я НЕ синхронизирую потоки сообщениями, я передаю сообщения о том что определённый пользователь запросил выполнить определённые действия. Один поток обеспечивает работу при разрывах соединения и обеспечивает отсутствие тормозов при плохой связи, другой отвечает за связь с базой данных.
3. Поиск соответствия я произвожу следующим образом. Каждой паре потоков у меня присваивается некий идентификатор - ClientID. Есть третий поток - поток сервиса, который порождает потоки соединений, они сообщают ему (потоку сервиса) что надо сделать то-то или то-то, в т.ч. и создать (и/или запустить) поток ответственный за взаимодействие с б.д.
Там же, в основном потоке сервиса есть список TList блокируемый TMultiReadExclusiveWriteSynchronizer"ом - он туда заносит регистрационные данные для существующих потоков общения с б.д. (пишет). Созданный Socket поток читает от туда нужную инфу, и если надо передаёт команду уже потоку БД непосредственно.
Т.е. если проводить аналогию - поток сервиса это CPU, поток сокета это память, поток базы это винт, а механизм общения аналогичен режиму DMA :-) т.е. я стараюсь максимально разгрузить основной поток сервиса.
← →
Eraser © (2006-03-24 23:04) [14]
> Lucefer (24.03.06 23:00) [13]
> В прниципе у созданного треда нет ни окна ни какой бы то
> ни было оконной функции, и по какому сообщению выход делать
> значения, на мой взгляд не имеет, можно хоть на WM_PAINT
> :-) Хотя WM_QUIT - меньше путаницы вносит. Я прав?
не прав ;-) Говорю же - Рихтера читай.
← →
Lucefer (2006-03-25 18:53) [15]Читаю. Но там много, поэтому не всё сразу, приходится наступать на грабли.
← →
Lucefer (2006-03-25 19:15) [16]И ещё один вопрос: надо убить себя, если к потоку не было отправлено ни одно сообщение в течении указанного времени. Как нибудь без таймеров это можно сделать? Или в Execute надо создавать таймер а потом его убивать? И не будет ли в таком случае проблемы если в результате выполнения встретится sleep(), на сколько я знаю время проведённое в sleep таймером не учитыватеся?
← →
Eraser © (2006-03-25 23:25) [17]
> Lucefer (25.03.06 19:15) [16]
> Или в Execute надо создавать таймер а потом его убивать?
что-то вроде того.
> И не будет ли в таком случае проблемы если в результате
> выполнения встретится sleep(), на сколько я знаю время проведённое
> в sleep таймером не учитыватеся?
именно так.
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2006.06.25;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.012 c