Текущий архив: 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.55 MB
Время: 0.009 c