V rámci prověřování jednoho nápadu jsem chtěl vyzkoušel jak se chová TThread.Queue a TThread.Synchronize (více o těchto volání z dřívějška) u ne GUI aplikací (resp. zkoušel jsem jen konzolové).
Mějme v rámci běhu vlákna volání přes uvedené dvě metody (primárně se mají používat k synchronizaci UI z kontextu hlavního vlákna, ale řekněme, že s tím máme jiný záměr a nepoužijeme jiné synchronizační prostředky jako je např. kritická sekce, nebo TMonitor nebo …).
V rámci uvedených volání Delphi RTL zařadí vaše volání do fronty a podle varianty buďto pokračuje, nebo čeká na provedení. V GUI aplikacích hlavní vlákno (myslím, že v OnIdle, volá Classes.CheckSynchronize, které provede Vaše volání). Jenže u konzolových aplikací nic takového nemáme, resp. můžeme mít. Naštěstí se někdo u Delphi zamyslel a nechal vrátka pootevřena. Volání Queue předtím volitelně zkusí zavolat nastavenou (pokud je) Classes.WakeMainThread (což je metoda typu TNotifyEvent). Tato metoda musí pak zavolat uvedené Classes.CheckSynchronize a tím je hotovo.
Pokud stále toto bereme jako intelektuální cvičení tak:
program httpconsole;
//{ $APPTYPE CONSOLE}
uses
SysUtils, Windows, Messages, Classes, http;
var
oThread: TTCPHttpDaemon;
const
WM_NULL= WM_USER + 100;
procedure WakeConsole(Sender: TObject);
begin
PostThreadMessage(System.MainThreadID, WM_NULL, 0, 0);
end;
var
WakeMainThread : TMethod = (Code:@WakeConsole; Data:Nil);
procedure WatchThreadFunc;
var
Msg: TMsg;
begin
Classes.WakeMainThread := TNotifyEvent(WakeMainThread);
repeat
GetMessage(Msg, 0, 0, 0);
DispatchMessage(Msg);
if Msg.Message = WM_NULL then
begin
Classes.CheckSynchronize;
end;
until (Msg.Message = WM_QUIT);
end;
begin
try
oThread := TTCPHttpDaemon.create;
// writeln('Start');
WatchThreadFunc;
oThread.Terminate;
oThread.WaitFor;
oThread.Free;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Na začátku vytvoříme vlákno TTCPHttpDaemon (z nejmenovaného dema nejmenované TCP/IP knihovny) a v rámci jeho běhu voláme Queue, resp. Synchronize.
Zajímavější je WatchThreadFunc, kdy v rámci hlavního vlákna aplikace spustíme smyčku zpráv s čekáním na naši definovanou zprávu WM_NULL (a při jejím odchycení voláme Classes.CheckSynchronize).
Zde bych rád speciálně upozornil na konstrukci
var
WakeMainThread : TMethod = (Code:@WakeConsole; Data:Nil);
…
Classes.WakeMainThread := TNotifyEvent(WakeMainThread);
Tímto vytvoříme proměnnou typu metoda (tj. "proceduru objektu"), kdy objekt je nil, ale je to kompatibilní s uvedeným přiřazením. Funkce WakeConsole má parametry jako TNotifyEvent (tj. standardní obsluha např. tlačítka), jen jsme z ní udělali metodu.
No a WakeConsole pak jen pošle hlavnímu vláknu zprávu, na kterou čekáme.
Velmi by mne zajímal váš názor v komentářích, případně vylepšení nebo jiné řešení.