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

Вниз

Передача сообщения из порождённого TThread в родительский сервис   Найти похожие ветки 

 
Lucefer   (2006-03-15 23:18) [0]

Создаю сервис, в котором объект TServerSocket порождает потоки для подключаемых клиентов. По окончании обработки эти потоки должны передать некую команду породившему их потоку. Когда то это было нечто вида
PostMessage(Application.MainForm.Handle,
RM_USER_LOGOUT,
Integer(DuplicateStrToPChar(strClientMessage)),
Integer(DuplicateStrToPChar(strClientID)));
-------------------------
Как это правильно сделать теперь? Желательно сделать это именно с помощью механизма сообщений, а не вызовом функции через Synchronize (сильно затормозит работу).


 
Reindeer Moss Eater ©   (2006-03-15 23:25) [1]

И в чем затруднение?
Нет Application.MainForm?

Дык создавая вторичные потоки передавай им хендл нужного окна и все.


 
Eraser ©   (2006-03-15 23:29) [2]


> Lucefer   (15.03.06 23:18)

1. Использовать событие OnTerminate класса TThread, но код этого события будет выполняться в основном потоке приложения, а не в потоке сервиса.
2. Использовать сообщения... ты сам привёл пример как это делать.
Если в сервисе нету окна, то можно организовать собственный цикл сообщений и отправлять их PostThreadMessage.
3. Передавать через переменные или поля объекта, защищая их критическими секциями или объектами синхронизации ядра.


 
Reindeer Moss Eater ©   (2006-03-15 23:33) [3]

Проще всего AllocateHWnd и метод класса сервиса в качестве оконной процедуры. Ничего не придется переделывать.


 
Lucefer   (2006-03-16 00:24) [4]

Спасибо за участие. Отвечаю по порядку.
1. Поскольку это сервис - окон нету вообще как класса. Соответственно нет оконных процедур, которые могли бы обработать сообщения.
2. Использовать OnTerminate не могу, так как потоки кэшируются, а не уничтожаются. Да и события происходят разные  - не просто процесс закончился - а закончился из-за за того что... (неправильный пароль, заблокированный IP, запрос к отсутствующим пока данным и т.д.) и параметр, например ID-клиента, IP-клиента, и т.д.
3. Использовать критические секции и всякие MREWS я уже думал. Но тогда придётся самому создавать список, добавлять туда сообщения из потоков, а из главного потока удалять элементы (что бы очередь не терялась). При этом как-то отслеживать поступление нового сообщения. На первый взгляд слишком мудрено.
4. По поводу PostThreadMessage - мне кажется это самое то, но есть несколько вопросов с которыми я пока не разобрался. А именно: (дальше текст из MSDN)

1. The thread to which the message is posted must have created a message queue, or else the call to PostThreadMessage fails.
2. The system only does marshalling for system messages (those in the range 0 to WM_USER). To send other messages (those above WM_USER) to another process, you must do custom marshalling.

Как в потоке создать этот message queue, причём так что бы он не загружал систему на 100% и не зацикливался если сообщение по каким-то причинам не придёт (тут наверное можно поставить выход при проверке на Terminate?)


 
Reindeer Moss Eater ©   (2006-03-16 00:27) [5]

Чем тебя не устроил AllocateHWnd?
Самый простой способ.


 
Lucefer   (2006-03-16 00:28) [6]

>Reindeer Moss Eater
>Проще всего AllocateHWnd и метод класса сервиса в качестве оконной процедуры.
---
Это как?

Т.е. вот у меня ServiceExecute уже осуществляет цикл
procedure TSomeService.ServiceExecute(Sender: TService);
begin
   FL.AppendLogRec("Service started");
   LoadConfig;
   FL.AppendLogRec("Config loaded");
   ServerSocket1.Port := ListenPort;
   ServerSocket1.ServerType := stThreadBlocking;
   ServerSocket1.ThreadCacheSize := 20;
   ServerSocket1.Active := True;
   while not Terminated do begin
     ServiceThread.ProcessRequests(True);
     sleep(0);
   end;
   ServerSocket1.Active := False;
   FL.AppendLogRec("Service stopped");
end;


 
Lucefer   (2006-03-16 00:37) [7]

Т,е. как я понимаю AllocateHWnd - создаст виртуальное окно.
1. Я создаю метод procedure TSomeService.FakeWndProc(var Msg: TMessage);
в котором буду обрабатывать свои сообщения (WM_USER+...)
2. на ServiceExecute получаю дескриптор этого виртуального окна AllocateHWnd и передаю его в создаваемый TThread, откуда и вызываю сообщения PostMessage.


 
Reindeer Moss Eater ©   (2006-03-16 00:38) [8]

function AllocateHWnd(Method: TWndMethod): HWND;

type TWndMethod = procedure(var Message: TMessage) of object;

Создаешь в классе сервиса метод-процедуру типа TWndMethod

Вызываешь AllocateHWnd с параметром - созданным методом. Получаешь хендл окна.
При создании вторичных потоков передаешь им этот хендл.
Ему потоки и будут постить сообщения.
Обрабатывать их будешь в созданном методе.


 
Eraser ©   (2006-03-16 00:53) [9]


> Reindeer Moss Eater ©   (16.03.06 00:27) [5]
>
> Чем тебя не устроил AllocateHWnd?
> Самый простой способ.

однако здесь опять же требуется чтобы в потоке был цикл выборки сообщений.

> Lucefer   (16.03.06 00:24) [4]


> Как в потоке создать этот message queue, причём так что
> бы он не загружал систему на 100% и не зацикливался если
> сообщение по каким-то причинам не придёт (тут наверное можно
> поставить выход при проверке на Terminate?)

например так:

   while GetMessage(Msg, 0, 0, 0) and (not Terminated) do
   begin
     if Msg.message = AM_PLAY_BACK then
     begin
        // some actions
     end;
     if Msg.message = WM_TIMER then
     begin
        // some other actions
     end;
   end;


 
Eraser ©   (2006-03-16 00:57) [10]


> Eraser ©   (16.03.06 00:53) [9]


> однако здесь опять же требуется чтобы в потоке был цикл
> выборки сообщений.

точнее обработка будет происходить в главном потоке, что не всегда хорошо.


 
Lucefer   (2006-03-16 08:30) [11]

И ещё я вот подумал - существует вероятность что сообщения в очередь не поставятся (по каким-то либо, одной винде известным причинам), а значит если я  для передачи параметра вызывал getmem - произойдёт утечка памяти, что не есть гуд.
Похоже лучше всего будет обернуть TList в критическую секцию, а из потока добалять туда новую команду и выставлять TEvent :(


 
Сергей М. ©   (2006-03-16 09:05) [12]


> существует вероятность что сообщения в очередь не поставятся


Нет такой вероятности.

Зато есть вероятность при собственной неверной организации цикла ожидания/выборки/обработки сообщений потерять это сообщение, тем самым допустив утечку из-за невыполнения соответствующего FreeMem


>  и выставлять TEvent


В любом случае потребуется цикл, ожидающий либо сообщения, либо срабатывание ивента, либо и то и другое


 
tesseract ©   (2006-03-16 10:09) [13]

А про DDE/named pipes/shared mem почему-то никто не упомянул. Хотя вроде для и предназначены


 
Сергей М. ©   (2006-03-16 10:13) [14]


> tesseract ©   (16.03.06 10:09) [13]


А не слишком ли жирно будет использовать DDE/named pipes/shared mem в контексте одного и того же процесса ?
Автор же не ведет речь об интерпроцессном взаимодействии, только  - о межпоточном инф.обмене внутри одного и того же "своего" процесса


 
Lucefer   (2006-03-16 11:22) [15]

Сделал так:

TSomeService = class(TService)
protected
   PMWinHandle :HWND;
   procedure FakeWndProc(var Msg: TMessage);
...
end;

procedure TSomeService.ServiceCreate(Sender: TObject);
begin
 PMWinHandle      := AllocateHWnd(FakeWndProc);
end;

procedure TSomeService.ServiceDestroy(Sender: TObject);
begin
 DeallocateHWnd(PMWinHandle);
end;

procedure TSomeService.ServerSocket1GetThread(Sender: TObject;
 ClientSocket: TServerClientWinSocket;
 var SocketThread: TServerClientThread);
begin
 //Передаю hwnd на псевдоокно
 SocketThread := TIOServerThread.Create(FALSE, ClientSocket, PMWinHandle)
end;

procedure TSomeService.ServiceExecute(Sender: TService);
begin
   ...
   ServerSocket1.Port := ListenPort;
   ServerSocket1.ServerType := stThreadBlocking;
   ServerSocket1.ThreadCacheSize := 20;
   ServerSocket1.Active := True;
   while not Terminated do begin
     ServiceThread.ProcessRequests(True);
     sleep(0);
   end;
   ServerSocket1.Active := False;
end;

procedure TSomeService.FakeWndProc(var Msg: TMessage);
begin
 with Msg do begin
   case Msg of
     CM_CLIENT_IS_ACTIVE: begin
           ...
       end;
     CM_CLIENT_ADDCOMMAND: begin
           ...
       end;
     ...
   else
     Result := DefWindowProc(PMWinHandle, Msg, wParam, lParam);;
   end;
 end;
end;


Всё работает. По поводу невозможности потери сообщений. :)))
Читаем MSDN:

Therefore, if the recipient thread is in a modal loop (as used by MessageBox or DialogBox), the messages will be lost. To intercept thread messages while in a modal loop, use a thread-specific hook.

Ну и ещё ограничение очереди по кол-ву сообщений, влияние приливов-отливов и т.д.


 
Сергей М. ©   (2006-03-16 11:45) [16]


> Читаем MSDN


Опять же - код модально вылупившихся диалогов попросту игнорирует эти сообщения, поэтому они и теряются ... Но поступают-то они в очередь исправно !


> ограничение очереди по кол-ву сообщений


А нефих слать самому себе огромную себе кучу сообщений и не выбирать их при этом своевременно)


> sleep(0);


Хоть и не мешает оно, но вообще-то здесь оно лишнее - в ходе исполнения ProcessRequests(True) тред вызывает как минимум 1 ф-цию ожидания, переводящую его в kernel-time, и этого достаточно.


 
Lucefer   (2006-03-23 15:38) [17]

А как правильнее организовать цикл выборки сообщений в TTHread для передачи ему сообщения из потока сервиса (через PostThreadMessage)?


 
Сергей М. ©   (2006-03-23 15:55) [18]

Обычным образом:

TMyThread = class(TThread)
..
  procedure SomeServiceMessageHandler(var Msg: TMessage); message SM_SOMEMSGCODE;
..
end;

var
Msg: TMsg;
...
while not Terminated and GetMessage(Msg, 0, 0, 0) do
  Dispatch(Msg.Message);

//если есть опасность мемликов
//здесь можно выбрать оставшиеся в очереди (необработанные) сообщения SM_SOMEMSGCODE

while PeekMessage(Msg, ThreadId, SM_SOMEMSGCODE, SM_SOMEMSGCODE, PM_REMOVE) do...


 
Игорь Шевченко ©   (2006-03-23 15:56) [19]


> А как правильнее организовать цикл выборки сообщений в TTHread


А какие есть варианты ?


 
Lucefer   (2006-03-23 23:10) [20]

Наверное стоит новую тему начать.


 
Lucefer   (2006-03-23 23:58) [21]

http://delphimaster.net/view/4-1143146463/



Страницы: 1 вся ветка

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

Наверх




Память: 0.53 MB
Время: 0.043 c
4-1142453912
Lucefer
2006-03-15 23:18
2006.06.25
Передача сообщения из порождённого TThread в родительский сервис


3-1146332166
Express
2006-04-29 21:36
2006.06.25
ADOCommand – как перейти на следующую запись???


2-1149606680
vegarulez
2006-06-06 19:11
2006.06.25
Как отчистить канву в image?


10-1119950348
VladR
2005-06-28 13:19
2006.06.25
IHTMLDocument2 извлечение ссылок с кирилличными символами


3-1146574237
ZABor
2006-05-02 16:50
2006.06.25
Грид.столбец = таблица.столбец и таблица.столбец1