Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.06.25;
Скачать: CL | DM;

Вниз

Вопросы по обмену сообщениями между 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 вся ветка

Текущий архив: 2006.06.25;
Скачать: CL | DM;

Наверх




Память: 0.53 MB
Время: 0.041 c
2-1149704397
Mr tray
2006-06-07 22:19
2006.06.25
в каком модуле содержится MAKELANGID?


2-1149580120
JTAG
2006-06-06 11:48
2006.06.25
Господа подскажите плз, где можно иконки для программы поискать


15-1149242870
Вопрошающий
2006-06-02 14:07
2006.06.25
Алгоритм определения дня недели по дате


15-1148984742
Nic
2006-05-30 14:25
2006.06.25
Курсовик по VBA ...


2-1149587403
Fiallo4ka
2006-06-06 13:50
2006.06.25
Базы данных Delphi