PDA

Visualizza la versione completa : [DELPHI] Critical Section


denis76
20-04-2009, 18:23
Mi serve il parere di una persona che ne sa davvero, non capisco cosa sto sbagliando o comunque cosa sta succedendo. Volevo mettere un post su http://www.delphiedintorni.it ma oggi non funziona.

Ho un'applicazione multithread che tra le varie cose scrive su un file di testo ciò che accade. La procedura
che scrive sul file è sottoposta a CriticalSection di modo che i threads non aprano contemposaneamente il file di log insieme.
A volte capita (tipo una volta al mese) che un thread venga tenuto bloccato dalla CriticalSection a lungo, oggi addirittura per 20 minuti. Poi scrive ciò che deve e prosegue il suo lavoro come nulla fosse. Il fatto è che in quei 20 minuti comunque gli altri threads hanno eseguito la procedura correttamente per cui la CriticalSection non era bloccata ma è stato il thread a non accorgersi di quando la procedura si è liberata.
Può essere che il sistema operativo sospenda l'esecuzione di un thread quando è oberato di lavoro proprio quando il thread incontra una CriticalSection bloccata?

Io uso le CriticalSection così:


...OnCreate()
InitializeCriticalSection(TCSLog);
...


procedure .....
begin
EnterCriticalSection(TCSLog);
...
...
LeaveCriticalSection(TCSLog);
end;

ESSE-EFFE
20-04-2009, 18:52
Non sarò di grande aiuto, ma spero di darti qualche idea.

Prima cosa: come mai non utilizzi la classe TCriticalSection?

Secondo, anche se ovviamente non è questo che causa il problema da te descritto, il codice tra EnterCriticalSection e LeaveCriticalSection è protetto da eventuali eccezioni vero?

Infine, i thread che utilizzano la classe per generare il LOG hanno almeno una Sleep nel loro ciclo? Sono TThread o li crei in altro modo?

HTH,

denis76
21-04-2009, 09:43
TCSLog è un oggetto TCriticalSection
Il programma è pieno di cicli try Except.

Il programma


var
TSNTPServer: TIdSNTP;
TDTOraInternet: TDateTime;
begin
SincLog(NomeFile, 'SorgenteDataOra()'); // Riga 1
if Base.SincroniaSNTP then begin // era false
end else begin
TDTOraInternet:= Now;
DateTime:= IncMinute(TDTOraInternet, TimeDelay); // TimeDelay era 0
SincLog(NomeFile, 'Uso DateTime dal Server locale -> '+ DateTimeToStr(TDTOraInternet, MySettings)+ '. Incremento di minuti: '+ IntToStr(TimeDelay)+ ', ottengo: -> '+ DateTimeToStr(DateTime, MySettings)); // Riga 2
...
end;

ha impiegato 20 minuti per andare dalla riga 1 alla riga 2, se pur eseguendo perfettamente il codice. La procedura che contiene la CriticalSection si chiama SincLog();

Non so cosa fare, il programma comunque cammina per mesi è che a volte si comporta così.

ESSE-EFFE
21-04-2009, 12:08
TCSLog è un oggetto TCriticalSection


Allora non capisco perchè non usi i relativi metodi Enter e Leave.



Il programma è pieno di cicli try Except.


In questo caso mi riferivo ovviamente al codice protetto dalla critical section.



La procedura che contiene la CriticalSection si chiama SincLog();


Giusto per conferma: la critical section comunque è allocata fuori dalla SincLog vero?

Come tu stesso dicevi, è probabile che non sia la critical section a bloccare quel thread. E nel codice che hai postato mi pare che l'unica cosa su cui indagare sia Base.SincroniaSNTP.

HTH,

alka
13-05-2009, 17:36
Direi che sono stati forniti già ottimi suggerimenti, quindi aggiungo solo un paio di considerazioni al problema.

La protezione del codice nella critical section va fatta usando try...finally:



EnterCriticalSection(TCSLog);
try
...
...
finally
LeaveCriticalSection(TCSLog);
end;


Questo garantisce che l'oggetto venga rilasciato anche in caso di errori, impedendo un "deadlock" dell'applicazione.

In merito al problema specifico, credo che ci sia da indagare maggiormente sulle operazioni svolte dal programma per saperne di più: il ritardo potrebbe essere dovuto a qualcosa che non è strettamente legato al multithreading.

Ciao! :ciauz:

ESSE-EFFE
13-05-2009, 20:35
La protezione del codice nella critical section va fatta usando try...finally:


Non necessariamente. Va bene anche un Try...Except nel qual caso ovviamente la critical section va sbloccata dopo il blocco Except.

denis76
13-05-2009, 22:38
Tutto vero per carità ma la cosa non mi aiuta.
Ribadisco che errori non ne vengono dati, il programma alla chiusura non segnala alcun memory leak e quant'altro.

alka
18-05-2009, 14:46
Originariamente inviato da ESSE-EFFE
Non necessariamente. Va bene anche un Try...Except nel qual caso ovviamente la critical section va sbloccata dopo il blocco Except.
In questo caso, può andare bene; in generale, non si tratta dello scopo specifico del "try...except", poiché

1)
è il costrutto "try...finally" ad esprimere, in generale, il concetto "qualunque cosa succeda, fai...";

2)
all'atto pratico, non è possibile inserire un raise nel blocco di gestione dell'eccezione senza modificare il codice restante, poiché in questo caso il rilascio della CriticalSection non verrebbe eseguita;

3)
usando la "try...except", è obbligatorio occuparsi logicamente e fattivamente della gestione dell'errore, cosa che non sempre è voluta o si vuole affrontare (o almeno, non in quella sede specifica, ma magari in un punto superiore del "call stack").

In breve, usare sempre il costrutto "try...except" per garantire il rilascio di risorse è fuorviante: serve per la gestione degli errori, quindi va inserito solo se si intende farla in questo contesto, lasciando al blocco "try...finally" il compito di racchiudere e separare i blocchi protetti di codice da quelli preposti al rilascio delle risorse allocate, indipendentemente dalla presenza - o meno - di codice per la gestione degli errori, che potrebbe anche non essere presente.

Ciao! :ciauz:

alka
18-05-2009, 14:48
Originariamente inviato da denis76
Ribadisco che errori non ne vengono dati, il programma alla chiusura non segnala alcun memory leak e quant'altro.
Con quale strumento hai verificato la mancanza di memory leak?

denis76
18-05-2009, 14:56
EurekaLog 6.0.17 su BDS 2006

Loading