Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "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
2-1149517101
сашки
2006-06-05 18:18
2006.06.25
нетипизированные файлы


3-1146214759
alsov
2006-04-28 12:59
2006.06.25
Надежность FireBird Embedded


15-1149185752
Grozz
2006-06-01 22:15
2006.06.25
задачка про SQL


9-1131449174
VolanD666
2005-11-08 14:26
2006.06.25
Frustum in D3D


1-1147859129
Damager
2006-05-17 13:45
2006.06.25
Интерфейс программ





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский