Newsgroup: it.comp.lang.delphi
Titolo: Thread e Synchronize

Avrei bisogno di un chiarimento od eventualmente qualche link esplicativo
sui thread.
Il mio dubbio è questo:
Ho un thread primario (quello dell'applicazione) con un Server Indy che
processa costantemente ed a intervalli irregolari delle richieste da più
Clients e scrive e legge in un database.
Ho poi un thread secondario che si attiva ogni tot, e dal suo metodo Execute
chiamo con Syncronize() una procedura che legge nello stesso database  ed
esegue dei controlli in base ai quali decide se rimuovere dei record o
modificarli.
In pratica entrambi i thread leggono e scrivono nelle stesse tabelle.
Mi è già stato consigliato di utilizzare un TCriticalSection e lo sto
studiando ma vorrei capire se,  quando dal thread secondario chiamo una
procedura con Syncronize(), quindi se ho capito bene facendola eseguire dal
thread primario,  tutte le procedure dello stesso thread primario vengono
sospese in attesa che termini l'esecuzione della chiamata con Synchronize(),
dunque anche la procedura Execute del Server,
Se così fosse, vorrei capire, in questo caso, lo scopo di utilizzare il
TCriticalSection.
Grazie a chi volesse schiarirmi le idee.
Valter

Scusa, Valter.
I thread non hanno ALCUNA NECESSITA', imho, di accedere al db usando la 
stessa entita, la qual cosa richiede il sincronismo a livello di thread 
via critical section, semafori etc.
Se i thread usano la stessa tecnologia di accesso ai dati crea una tua 
classe thread che incopora una connection ed una query (tadoconnection 
+tadoquery se usi ado, tsqlconnection + tsqlquery se usi dbx etc etc etc.
Poi, erediti da questa i 2 thread che ti servono.
A te serve che i thread leggano le stesse tabella, giusto?
Ma cio' molto diverso da "i thred devono leggere le stesse tabelle 
condividendo (ovvero, facendo a chi arriva prima...) le parti di codice 
necessarie per le prp operazioni.
Le critical section sono uno strumento valido per altri contesti.
Ad esempio, io le ho usate in una cosa molto simile al tuo caso, con la 
differenza che non gestisco tabelle di db, ma una lista di oggetti in 
memoria.
Ma tcritical section è un blocco che impedisce sia lettura che scrittura.
Esistono alternative che permetteono il blocco solo in scrittura ma la 
lettura è sempre attiva.
A.

 ha scritto nel messaggio 
news:Gtw_g.5500$pL3....@tornado.fastwebnet.it...

Se usi Syncronize il thread principale viene bloccato sempre, qualunque cosa 
stia facendo, anche ad esempio se gestisce un repaint. Col Critical section 
il thread principale viene bloccato solo e solamente nel caso che stia 
entrando anche lui in un TCritical section, ad esempio proprio in quel 
momento tenta di scivere i dati, mentre il secondario era gia' entrato nel 
Tcritical section per leggerli.
In sostanza si ha il blocco di uno dei due, in ordine temporale il secondo, 
quando entrambi tentano contemporaneamente di accedere ai dati entrando 
all'interno del tcriticalsection.
Giorgio 

Ma tcritical section è un blocco che impedisce sia lettura che scrittura.
Non è vero.  La critical section di per sé non entra nel merito.
Una CS, come qualsiasi altro oggetto di sincronzizzazione, viene usata
per segnare l'istante dal quale le elaborazioni procedono
serializzate, a prescindere poi da cosa si voglia fare realmente.
Se non voglio bloccare una lettura, allora i thread non acqusiranno la
cs, così come se non voglio bloccare una scrittura.

chiamo con Syncronize() una procedura che legge nello stesso database
L'accesso ai dati è *gia* "sincronizzato" dal DBMS.  Che bisogno hai
di aggiungere un altro strato di sincronizzazione ?

 A te serve che i thread leggano le stesse tabella, giusto?
Quasi. Il thread creato è uno solo, l'altro è l'applicazione stessa.
Nell'applicazione è avviato un TIdTCPServer che nel suo metodo Execute invia
e riceve dati. Ad ogni connessione di un client verifica nel db se è un
nuovo utente o se è gia presente...se è nuovo inserisce un nuovo record di
tre campi - Username, ora di connessione, dati che andrà ad elaborare -,
altrimenti modifica il campo esistente "ora di connessione" e "dati da
elaborare".
Fermandomi qui non avrei bisogno di chiarimenti.
Ma io devo verificare saltuariamente (metti ogni ora) se i client che hanno
ricevuto dati da elaborare si sono ripresentati o meno (l'utente potrebbe
aver spento il pc, aver subìto un crash) e se così non è ridistribuisco di
nuovo gli stessi dati ad altri client sempre online (in questo modo non
perdo "pezzi"...)
Il thread secondario dunque, ogni ora accede al db, confronta per ogni
utente presente in tabella l'ora di ultima connessione con quella attuale e
se qualche utente non è più tornato assegna i suoi dati al primo client che
ri ripresenta e sposta l'utente perso in un'altra tabella. Questo utente
eliminato perchè in timeout me lo gestisco dall'applicazione, che se si
accorge che è rientrato lo elimina dalla tabella *utenti persi* e lo
reinserisce nella *utenti online*.
E' un pò come utilizzare un TTimer ma pensavo, forse a torto, che il thread
avrebbe frizzato il resto delle operazioni.
E' tutto qua. Quindi mentre il thread esegue questa verifica non devono
essere processati dati nel db da parte dell'applicazione.
Il Synchronize() non è dunque sufficiente?
V.

ricevuto dati da elaborare si sono ripresentati o meno[...]
No, il vero problema del server è che i "pacchetti" vengano elaborati,
quindi anziché testare che i client si siano "ripresentati", non deve
far altro che assegnare un time-out di elaborazione per ogni
pacchetto.
Se i client A, B e C mi chiedono dei dati, io gli mando i pacchetti 1,
2, 3 e segno per ogni pacchetto la data/ora di spedizione (il
significato di "stato" è NE=non elaborato, EL=elaborato, SC=scaduto):
pacchetto  | data/ora spedizione  |  stato  | assegnato a |
-------------------------------------------------------------
   1          22/10/2006 13.26.13     NE         A
   2          22/10/2006 13.26.14     NE         B
   3          22/10/2006 13.26.15     NE         C
se A e C rispondono con l'elaborazione dei relativi pacchetti entro il
tempo limite (un'ora, supponiamo), allora io segno l'elaborazione dei
relativi pacchetti e dò loro un altro pacchetto, se c'è:
pacchetto  | data/ora spedizione  |  stato  | assegnato a |
-------------------------------------------------------------
   1          22/10/2006 13.26.13     EL         A
   2          22/10/2006 13.26.14     NE         B
   3          22/10/2006 13.26.15     EL         C
   4          22/10/2006 13.33.23     NE         A
   5          22/10/2006 13.50.23     NE         C
Se il client D si presenta a me e richiede un pacchetto, io verifico
innanzitutto che non vi siano pacchetti non elaborati da più del tempo
limite (un'ora) e se vi sono assegno il pacchetto al nuovo client e lo
segno scaduto per il vecchio:
pacchetto  | data/ora spedizione  |  stato  | assegnato a |
-------------------------------------------------------------
   1          22/10/2006 13.26.13     EL         A
   2          22/10/2006 13.26.14     SC         B
   3          22/10/2006 13.26.15     EL         C
   4          22/10/2006 13.33.23     NE         A
   5          22/10/2006 13.50.23     NE         C
   2          22/10/2006 14.30.14     NE         D
Se e quando B tornerà con i dati elaborati, il server lo ignorerà
perché ormai ha già segnato il suo pacchetto come scaduto.
Con questa logica, il server lavora solo quando il client richiede
pacchetti, non hai necessità di implementare timer.


E' un'ottima soluzione!
Ora risistemo tutto seguendo la tua logica...
:-))
grazie davvero
Valter


Anche se, come avrai letto dagli altri post, cambierò la struttura del
programma e quindi non utilizzerò almeno per questo caso il thread
secondario, almeno adesso ho la conferma di quanto già immaginavo su
Synchronize().
Grazie Giorgio
V.