|
[ Boris B. @ 07.03.2009. 10:16 ] @
| Imam jedan prilicno specifican problem, naime, poziv Windows API funkcije PeekMessage izazove da se sve pending poruke poslane sa SendMessage proslede WndProc funkciji.
Imam threadove koji imaju svaki svoj window handle i message loop, tj. komuniciraju medjusobno iskljucivo preko Send/Post message API funkcija. Sve radi fenomenalno, ali problem nastaje kada jedan thread ceka na window poruku drugog threada:
Code:
//thread koji ceka
var
Msg: tagMsg;
...
while (not PeekMessage(Msg, FHandle, WaitedMsg, WaitedMsg, PM_REMOVE or PM_NOYIELD)) and (not FThread.Terminated) do
Sleep(1);
//ovde imam poruku koju cekam
...
Samo cekanje radi, problem je sto dok thread ceka na poruku WaitedMsg, ako drugi thread posalje nesto sa SendMessage to ce se obraditi odmah (direktno se poziva WndProc), jer izgleda da jedan poziv PeekMessage izazove da se obrade sve poruke poslane sa SendMessage. Kako to da izbegnem? Ima li mozda neki drugi nacin da dodjem do svojih poruka a da ne koristim Peek ili Get Message?
|
[ savkic @ 07.03.2009. 18:03 ] @
> Imam threadove koji imaju svaki svoj window handle i message loop, tj. komuniciraju medjusobno iskljucivo preko Send/Post message API funkcija.
Pravio si poseban window handle za svaki thread, da li si koristio CreateWindow? Možeš li da koristiš thread queue umesto (ne treba poseban window, ali se poruke mogu samo postovati).
while (not PeekMessage(Msg, FHandle, WaitedMsg, WaitedMsg, PM_REMOVE or PM_NOYIELD)) and (not FThread.Terminated) do
Sleep(1);
//ovde imam poruku koju cekam
...
> Samo cekanje radi, problem je sto dok thread ceka na poruku WaitedMsg, ako drugi thread posalje nesto sa SendMessage to ce
> se obraditi odmah (direktno se poziva WndProc), jer izgleda da jedan poziv PeekMessage izazove da se obrade sve poruke poslane
> sa SendMessage. Kako to da izbegnem? Ima li mozda neki drugi nacin da dodjem do svojih poruka a da ne koristim Peek ili Get Message?
Dok traje izvršavanje PeekMessage procesiraju se poruke poslate sa SentMessage. Možeš pokušati da staviš PM_QS_POSTMESSAGE or PM_REMOVE kao flag, ako to ne pomogne moraćeš da revidiraš sistem slanja.
[ Boris B. @ 08.03.2009. 11:20 ] @
>Pravio si poseban window handle za svaki thread, da li si koristio CreateWindow?
Ne direktno, koristio sam Delphi-jev budz Classes.AllocateHWnd, koji kreira dummy window tipa UtilWindow pomocu CreateWindowEx. Rezultat je da dobijes pravi window handle i registurej svoj WndProc iz TObject naslednika, znaci ne moras da nasledjujes neefikasan TWinControl samo da bi imao window handle (Tako radi npr. TTimer koji je TComponent a ima window handle za WM_Timer poruku).
>Možeš li da koristiš thread queue umesto (ne treba poseban window, ali se poruke mogu samo postovati)
Ne mogu bas iz tog razloga, sto onda ne radi SendMessage. Kada mi svi threadovi imaju window handle onda mogu da radim i Send i PostMessage, Post kada hocu asinhrono (non-blocking) da pozivam metode drugih threadova i Send kada hocu sinhrono (blocking) da bih odmah dobio rezultat kada je to potrebno. Uposte ne moram da razimsljam o implementaciji metoda koji pozivam. Ceo sistem radi perfektno, jedino se kasnije pokazalo da mi je potrebno jos i cekanje na odredjenu poruku koja ce uvek stici sa PostMessage, znaci u message queue. Problem je u tome da dok se ceka na poruku preko onog koda thread nije blokira, ako neki thread posalje nesto sa SendMsg to se obradi odmah sto potencijalno dovede do jos jednog cekanja. Kada prvi odgovor konacno stigne, stigne u onu granu koja je poslednja cekala a ne prva.
>Možeš pokušati da staviš PM_QS_POSTMESSAGE or PM_REMOVE kao flag, ako to ne pomogne moraćeš da revidiraš sistem slanja.
PM_QS_POSTMESSAGE ne pomaze, probao sam. Isto tako ni critical sectioni ne pomazu (logicno, ne mozes da zakljucas samog sebe), a neki Boolean flegovi tipa FWaiting naravno izazovu deadlock threada koji ceka. Izgleda da cu stavrno moarati da revidiram sistem slanja, probacu sa TEventom.
[ savkic @ 08.03.2009. 16:10 ] @
> Ne direktno, koristio sam Delphi-jev budz Classes.AllocateHWnd, koji kreira dummy window tipa UtilWindow pomocu CreateWindowEx.
Pretpostavio sam da je tako, samo da te upozorim daAllocateHWnd nije threadsafe, u nekim retkim situacijama može doći do problema. Evo teksta koji to opširno objašnjava http://17slon.com/blogs/gabr/2...tehwnd-is-not-thread-safe.html
> tipa FWaiting naravno izazovu deadlock threada koji ceka. Izgleda da cu stavrno moarati da revidiram sistem slanja, probacu sa TEventom.
Ja sam nešto slično pravio, evo otprilike ideje:
Code:
unit ThreadUnit;
interface
uses
Windows, Classes, Messages;
const
MSG_SEND = WM_APP + 1;
MSG_CALC = WM_APP + 2;
type
TMyThread = class(TThread)
private
procedure ProcessMessages;
procedure MsgSend(var Msg: TMessage); message MSG_SEND;
procedure MsgCalc(var Msg: TMessage); message MSG_CALC;
protected
procedure Execute; override;
public
function SendMsg(Msg: UINT; wParam: Longint; lParam: Longint): Longint;
end;
implementation
type
TSendHelper = packed record
Msg: UINT;
lParam: Longint;
wParam: Longint;
Result: Longint;
Event: THandle;
end;
{ TMyThread }
procedure TMyThread.Execute;
var
msg: TMsg;
begin
PeekMessage(msg, 0, WM_NULL, WM_NULL, PM_NOREMOVE);
while not Terminated do
begin
Sleep(100);
ProcessMessages;
end;
end;
procedure TMyThread.MsgCalc(var Msg: TMessage);
begin
Msg.Result := Msg.LParam * Msg.WParam;
end;
procedure TMyThread.MsgSend(var Msg: TMessage);
var
Temp: TSendHelper;
TempMsg: TMessage;
begin
Temp := TSendHelper(Pointer(Msg.wParam)^);
TempMsg.Msg := Temp.Msg;
TempMsg.wParam := Temp.wParam;
TempMsg.lParam := Temp.lParam;
try
Dispatch(TempMsg);
Temp.Result := TempMsg.Result;
SetEvent(Temp.Event);
except
end;
end;
procedure TMyThread.ProcessMessages;
var
msg: TMsg;
Temp: TMessage;
begin
// Preuzimamo i procesiramo sve poruke koje se trenutno nalaze u message queueu ovog treada
while PeekMessage(msg, INVALID_HANDLE_VALUE, 0, 0, PM_REMOVE) do
begin
Temp.Msg := msg.message;
Temp.wParam := msg.wParam;
Temp.lParam := msg.lParam;
try
Dispatch(Temp);
except
end;
end;
end;
function TMyThread.SendMsg(Msg: UINT; wParam: Longint; lParam: Longint): Longint;
var
Temp: TSendHelper;
begin
Temp.Event := CreateEvent(nil, True, False, nil);
Temp.Msg := Msg;
Temp.lParam := lParam;
Temp.wParam := wParam;
PostThreadMessage(ThreadID, MSG_SEND, Longint(@Temp), 0);
WaitForSingleObject(Temp.Event, INFINITE);
Result := Temp.Result;
CloseHandle(Temp.Event);
end;
end.
I pozivanje:
FThread := TMyThread.Create(False);
Caption := IntToStr(FThread.SendMsg(MSG_CALC, 343, 222));
[ Boris B. @ 08.03.2009. 23:46 ] @
>samo da te upozorim da AllocateHWnd nije threadsafe
Znam, zato sam zamotao AllocateHWnd u utitlity funkciju sa critical section-om, i posto je koristi samo TTimer i moje klase onda nije problem
>Ja sam nešto slično pravio, evo otprilike ideje
U prinicipu to je to, TEvent resava posao, na svu srecu u mom serveru se samo na par mesta koristi cekanje na poruku tako da nece biti neki problem to implementirati.
Hvala za odgovor i savete.
[ priki @ 09.03.2009. 08:57 ] @
zašto ne koristiš GetMessage() i MsgWaitForMultipleObjects()
oni blokiraju thread ako nema poruka
tako thread ne "prži" procesor bez veze
[Ovu poruku je menjao priki dana 09.03.2009. u 10:42 GMT+1]
[ Rapaic Rajko @ 09.03.2009. 11:55 ] @
Moram da se zahvalim savkic-u na gore postovanom kodu; prava mala skolica.
Bio sam nacuo da thread-ovi mogu da komuniciraju medju sobom, ali nisam nikad pokusao (nije mi trebalo). Sve u svemu, ova tema mi je ulepsala jutro (hvala i Borisu) - uvek sam srecan kad nesto naucim :) .
Hvala jos jednom i pozdrav
Rajko
[ savkic @ 09.03.2009. 13:04 ] @
> zašto ne koristiš GetMessage() i MsgWaitForMultipleObjects()
> oni blokiraju thread ako nema poruka
Nema razlike između GetMessage i PeekMessage, dok čekaju obrađuju non-qued poruke (poruke poslate sa SendMessage, SendMessageTimeout...)
[ Boris B. @ 10.03.2009. 00:01 ] @
>zašto ne koristiš GetMessage() i MsgWaitForMultipleObjects() oni blokiraju thread ako nema poruka tako thread ne "prži" procesor bez veze
I koristim GetMessage u glavnom message loop-u, samo sam probao sistem za cekanje tj. taj mini-message-loop sa PeekMsg misleci da PeekMessage ne izaziva obradu poruka poslanih sa SendMessage, ali se ispostavilo da i Get i Peek izazivaju obradu poruka poslanih sa Send, sto je nezeljeni efekat u mom slucaju.
Druga stvar, "while True do Sleep(1)" ne przi procesor ama bas nista, jer se pri svakoj iteraciji thread suspenduje na jednu minimalnu rezoluciju tajmera (obicno oko 16ms). Sleep(0) sa druge strane pojede ceo procesor jer uopste ne prepusta procesorsko vreme ako nema drugog threada koji ceka na izvrsenje sa istim ili visim prioritetom. Znaci uvek Sleep(1) nikad Sleep(0) kao rule of thumb
>Nema razlike između GetMessage i PeekMessage, dok čekaju obrađuju non-qued poruke
Pa nije bas tako, PeekMessage ne ceka nista ali ipak izazove obradu non-queued poruka... 
[ savkic @ 10.03.2009. 02:11 ] @
>> Nema razlike između GetMessage i PeekMessage, dok čekaju obrađuju non-qued poruke
> Pa nije bas tako, PeekMessage ne ceka nista ali ipak izazove obradu non-queued poruka...
Pa dobro, dok čekaju da se završe ili dok se izvršavaju... ;)
Copyright (C) 2001-2025 by www.elitesecurity.org. All rights reserved.
|