threads & synchronization
6 отговора
0 прегледа
threads & synchronization
Здравейте,
имам нужда от малко помощ за threads и sync.
В момента положението е следното:
1. Опитал съм се да "развържа" UI-то от логиката - т.е. цялата логика е в един "core", който предоставя API, което се извиква от UI-то.
Имам само една инстанция на този core.
2. Имам два singelton-а (communicator и logger), които се ползват от този core съответно за изпращане на данни през нета
и за писане в лога (просто записвам е един файл за да имам инфо за диагностика на евентуални проблеми).
В момента всичко си работи, но има 1 нещо, което ме нервира доста и искам да го променя:
1. При даден action от УИ-то се извиква дадена функция от core-а.
В нормалния случай (от псевдокода по-долу: DoSomething, DoSomethingElse) всичко е ок.
Но при SendSometing() комуникацията може да е доста бавна (1,2,3 секунди) и UI-то ми заспива, което е доста досадно макар и да е за секунди.
Комуникацията е тип request/response - т.е. изпращам request-а и чакам докато дойде response-а:
примерно изпращам GET-заявка към някакъв webserver и получавам response.
Мислех си при core.SendSomething() и core.SendSomethingElse() да стартирам отделен thread,
който да се обръща към communicator-а и чака да дойде response.
През това време УИ-то няма да е замръзнало - само съответния action ще е disabled за известно време, докато thread-а свърши работата. за УИ-то не е фатално, ако резултата от комуникацията се покаже след известно време (в onTerminate на thread-a примерно)
Това което ме притеснява е следното:
Докато се изпълнява thread-а за SendSomething() от УИ-то може да се избере другия (SendSomethingElse),
който ползва същата (единствена) инстанция на communicator.
От друга страна от core-а и communicator-а ползват logger-а
И като са няколко thread-а подозирам, че ще стане мазало.
Ще се радвам, ако дадете едно рамо за това как да си гарантирам, че само един thread ще send-ва в даден момент и ще пише в лога в друг даден момент :)
Дано да съм бил ясен какво се опитвам да направя, защото половината от решението на даден проблем е доброто му описание ;)
имам нужда от малко помощ за threads и sync.
В момента положението е следното:
1. Опитал съм се да "развържа" UI-то от логиката - т.е. цялата логика е в един "core", който предоставя API, което се извиква от UI-то.
Имам само една инстанция на този core.
2. Имам два singelton-а (communicator и logger), които се ползват от този core съответно за изпращане на данни през нета
и за писане в лога (просто записвам е един файл за да имам инфо за диагностика на евентуални проблеми).
В момента всичко си работи, но има 1 нещо, което ме нервира доста и искам да го променя:
1. При даден action от УИ-то се извиква дадена функция от core-а.
В нормалния случай (от псевдокода по-долу: DoSomething, DoSomethingElse) всичко е ок.
Но при SendSometing() комуникацията може да е доста бавна (1,2,3 секунди) и UI-то ми заспива, което е доста досадно макар и да е за секунди.
Комуникацията е тип request/response - т.е. изпращам request-а и чакам докато дойде response-а:
примерно изпращам GET-заявка към някакъв webserver и получавам response.
Мислех си при core.SendSomething() и core.SendSomethingElse() да стартирам отделен thread,
който да се обръща към communicator-а и чака да дойде response.
През това време УИ-то няма да е замръзнало - само съответния action ще е disabled за известно време, докато thread-а свърши работата. за УИ-то не е фатално, ако резултата от комуникацията се покаже след известно време (в onTerminate на thread-a примерно)
Това което ме притеснява е следното:
Докато се изпълнява thread-а за SendSomething() от УИ-то може да се избере другия (SendSomethingElse),
който ползва същата (единствена) инстанция на communicator.
От друга страна от core-а и communicator-а ползват logger-а
И като са няколко thread-а подозирам, че ще стане мазало.
Ще се радвам, ако дадете едно рамо за това как да си гарантирам, че само един thread ще send-ва в даден момент и ще пише в лога в друг даден момент :)
TMainForm
OnActionDoSomething();
OnActionDoSomethingElse();
OnActionSendSomething();
OnActionSendSomethingElse();
//end
procedure TMainForm.OnActionDoSomething();
begin
core.DoSomething;
end;
procedure TMainForm.OnActionDoSomethingElse();
begin
core.DoSomethingElse;
end;
procedure TMainForm.OnActionSendSomething();
begin
core.SendSomething;
end;
procedure TMainForm.OnActionSendSomethingElse();
begin
core.SendSomethingElse;
end;
=====================================================
TCore
DoSomething();
DoSomethingElse();
SendSomething();
SendSomethingElse();
// end
function TCore.DoSomething(): boolean;
begin
logger.trace("doing something...");
....
logger.trace("done.");
end;
function TCore.DoSomethingElse(): boolean;
begin
logger.trace("doing something else...");
....
logger.trace("done.");
end;
function TCore.SendSomething(): boolean;
begin
logger.trace("sending something else...");
communicator.Send();
logger.trace("done.");
end;
function TCore.SendSomethingElse(): boolean;
begin
logger.trace("sending something else...");
communicator.Send();
logger.trace("done.");
end;
==================================================
TCommunicator
Send();
// end
procedure TCommunicator.Send();
begin
logger.trace('preparing request');
resp := send_data_via_socket(req);
logger.trace('response seceived');
end;
===============================
TLogger
Trace(); ---> adding to log.
// end
Дано да съм бил ясен какво се опитвам да направя, защото половината от решението на даден проблем е доброто му описание ;)
P.S: (threads & synchronization)
и дали стартирането на thread-a да е в core.Send....() или по-надолу в communicator.Send() ?
Re: P.S: (threads & synchronization)
Използвай семафори и мутекси и си защити кода в ядрото с критични секции.
Mixy
Mixy
Re: threads & synchronization
Един детайл липсва
- от какъв характер ти е лог-а.
Ако logger-а е не е свързан с UI:
Ако logger-а update-ва UI (напр. Memo), вече зависи как ще реализираш конците.
После пак ще пиша, че сега нямам време.
Междувременно req и resp глобални променливи ли са или са локални за TCommunicator.Send?
Също в TCommunicator.Send прави ли се нещо с отговора или само се предава нагоре? Сигурно обработката става в TCore след communicator.Send();?
Ако logger-а е не е свързан с UI:
TLogger
FCS: TCriticalSection;
Trace(); ---> adding to log.
// end
constructor TLogger.Create;
begin
FCS := TCriticalSection.Create;
end;
destructor TLogger.Destroy;
begin
FCS.Free;
end;
procedure TLogger.Trace(const msg : String);
begin
FCS.Enter;
try
// достъп до ресурса (напр. файл)
finally
FCS.Leave;
end;
end;
Ако logger-а update-ва UI (напр. Memo), вече зависи как ще реализираш конците.
После пак ще пиша, че сега нямам време.
Междувременно req и resp глобални променливи ли са или са локални за TCommunicator.Send?
Също в TCommunicator.Send прави ли се нещо с отговора или само се предава нагоре? Сигурно обработката става в TCore след communicator.Send();?
Re: threads & synchronization
Благодаря за отговорите до момента
- loggera не е свързан с УИ-то.
- req и resp са в core, а communitor-а от req прави малко подготовка на един "raw" req , който изпраща. и после от "raw" resp подготвя resp, който връща нагоре към core.
ето тук съм копирал промените, които направих (в зелено) спрямо началния вариант - видимо работи, но моля хвърлете едно око да не би да съм написал някоя голяма глупост дето не виждам.
- loggera не е свързан с УИ-то.
- req и resp са в core, а communitor-а от req прави малко подготовка на един "raw" req , който изпраща. и после от "raw" resp подготвя resp, който връща нагоре към core.
ето тук съм копирал промените, които направих (в зелено) спрямо началния вариант - видимо работи, но моля хвърлете едно око да не би да съм написал някоя голяма глупост дето не виждам.
=====================================================
TCore
DoSomething();
DoSomethingElse();
SendSomething();
SendSomethingElse();
// events
OnSomethingSent();
OnSomethingElseSent();
// end
function TCore.SendSomething(): boolean;
begin
logger.trace("sending something else...");
communicator.Send(@OnSomethingSent);
logger.trace("done.");
end;
function TCore.SendSomethingElse(): boolean;
begin
logger.trace("sending something else...");
communicator.Send(@OnSomethingElseSent);
logger.trace("done.");
end;
==================================================
TCommunicator
Send();
commLock: TCriticalSection; - създавам я в Create на communicator и я изчиствам когато го Free-вам
end;
procedure TCommunicator.Send(eventSent);
var
thr: TCommunicatingThread;
begin
thr := TCommunicatingThread.Create();
thr.OnTerminate := eventSent;
end;
TCommunicatingThread = class(TThread)
procedure TCommunicatingThread.Execute;
begin
logger.trace('preparing request');
commLock.Enter;
logger.trace('sending');
resp := send_data_via_socket(req);
logger.trace('sent');
commLock.Leave;
logger.trace('response seceived');
end;
==============================
TLogger
Trace();
logLock: TCriticalSection; - създавам я в Create на logger и я изчиствам когато го Free-вам
// end
procedure TLogger.Trace();
var
thr: TLoggingThread;
begin
thr := TLoggingThread.Create();
end;
TLoggingThread = class(TThread)
procedure TLoggingThread.Execute;
begin
logLock.Enter;
append_to_logfile();
logLock.Leave;
end;
Re: threads & synchronization
На мен ми изглежда OK така, със следните добавки:
Освен това в logger-а е напълно излишно да създаваш thread, достатъчно е просто да заградиш писането във файла с критична секция, както в предния ми пост.
С req и resp не би трябвало да имаш проблеми така, както е направено, защото OnTerminate на Thread-а се изпълява в контекста на главната нишка. Просто прегледай още веднъж за всеки случай, дали има вероятност 2 нишки да пипат в един и същи обект - от псевдо кода няма как де се види това, трябва реалният код.
function TCore.SendSomething(): boolean;
begin
logger.trace("sending something else...");
communicator.Send(@OnSomethingSent);
logger.trace("done."); // за съжаление, това ще се изписва
// преди да е приключила комуникацията -
// може би ще искаш да го преместиш някъде другаде
end;
==================================================
TCommunicator
Send();
commLock: TCriticalSection; - създавам я в Create на communicator и я изчиствам когато го Free-вам
end;
procedure TCommunicator.Send(eventSent);
var
thr: TCommunicatingThread;
begin
thr := TCommunicatingThread.Create(true); // suspended, за да можеш да си сигурен,
// че следващият ред ще се изпълни
thr.FreeOnTerminate := true; // иначе ще имаш memory leak
thr.OnTerminate := eventSent;
thr.Resume;
end;
TCommunicatingThread = class(TThread)
procedure TCommunicatingThread.Execute;
begin
logger.trace('preparing request');
commLock.Enter;
try
logger.trace('sending');
resp := send_data_via_socket(req);
logger.trace('sent');
finally // това е необходимо, защото, ако send_data_via_socket или logger.trace
// вдигне exception, критичната секция остава заключена
// и няма да можеш да пращаш нищо повече,
// докато не се рестартира програмата.
commLock.Leave;
end;
logger.trace('response seceived');
end;
Освен това в logger-а е напълно излишно да създаваш thread, достатъчно е просто да заградиш писането във файла с критична секция, както в предния ми пост.
С req и resp не би трябвало да имаш проблеми така, както е направено, защото OnTerminate на Thread-а се изпълява в контекста на главната нишка. Просто прегледай още веднъж за всеки случай, дали има вероятност 2 нишки да пипат в един и същи обект - от псевдо кода няма как де се види това, трябва реалният код.
Редактирано от Formal на
Re: threads & synchronization
extra,
мерси много :)
мисля, че това ми е предостатъчно и би трябвало да се оправя нататък