ISession di NHibernate in uno schenario multithread

rated by 0 users
This post has 4 Replies | 2 Followers

Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907
petrux Posted: 07-21-2010 18.13

Ciao a tutti,

ho una applicazione *molto* multithread. :-) Non è una applicazione web, ma è (più o meno) un servizio windows, quindi ha un ciclo di vita teoricamente infinito.
Diversi componenti hanno necessità di caricare e manipolare i dati,
anche contemporaneamente.
Tutti i componenti (quindi, anche attraverso threads diversi) accedono
alla stessa sessione.
Facendo girare qualche test, ho ottenuto (con distribuzione
assolutamente casuale) divesre eccezioni del tipo:

- NHibernate.Exceptions.GenericADOException: could not initialize a
collection ... System.InvalidOperationException: Index was outside
bounds of the array
- NHibernate.ADOException: There was a problem converting an
IDataReader to NDataReader ---> SYstem.InvalidOperationException:
Invalid attempt to call MetaData when reder is closed

che vengono generate, come già detto, casualmente.
Ora, mi chiedo, è possibile che sia la condivisione della sessione su
più thread a fare casino?
In generale, esistono delle "best practices" per la gestione di simili
scenari? E se creassi una sesssione per ogni thread?
In questo caso, visto che ho oggetti che fanno parte di relazioni *-*
che vanno in giro per il mondo (e quindi anche attraverso thread
differenti) non avrei sempre problemi con le sessioni?

Ciao e grazie,
Giulio

Top 10 Partecipanti
Maschio
Post 243
Punteggio 3.383

LA Session di nhibernate no è thread safe, per cui non la puoi accedere da diversi thread. La soluzione è usare un ciclo di vita "session per thread" ed ogni tanto devi anche disposare la sessione altrimenti il consumo di memoria è mostruoso.

Io lavoro spesso in scenari di questo tipo, windows service e multithreading, solitamente il servizio gestisce "operazioni" nel mio caso ho uno scheduler che esegue delle azioni, ed adotto.

Ogni thread ha la sua session, al termine di ogni azione disposo la sessione cosi la azione successiva ha una session nuova.

alk.

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907

Ciao Alk,

 

innanzitutto grazie per la risposta.

 

Gian Maria Ricci:

Io lavoro spesso in scenari di questo tipo, windows service e multithreading, solitamente il servizio gestisce "operazioni" nel mio caso ho uno scheduler che esegue delle azioni, ed adotto.

 

...adotti cosa?

 

 

Gian Maria Ricci:

Ogni thread ha la sua session, al termine di ogni azione disposo la sessione cosi la azione successiva ha una session nuova.

 

 

Alla fine, guardando bene il codice, ho isolato dei punti di thread safety, ho creato (più o meno) una sessione per thread e non ho più problemi. Inoltre uso le session solo per piccole operazioni localizzate, quindi non ho nemmeno un eccessivo utilizzo di memoria. Credo di essere sul punto di imbroccare la strada giusta (che è più un "credo di essere sul punto di acquisire la fomra mentis che mi serve)... ma ci sono ancora dei punti "oscuri". 

 

Cito, ad esempio, un problema che mi sto ponendo. Ho un modello molto "connesso", una specie di matrice fatta così:

class Matrix {

 IEnumerable <Column> ColumnSet { get; }
 IEnumerable <Row> RowSet { get; }
 void AddColumn(Column c);
 void AddRow(Row r);
}

class Column
{
 Matrix Matrix { ... }
 String Label { ... }
}

class Row
{
 Matrix Matrix { ... }
 IEnumerable<Element> ElementSet { get; }
  String Label { ... }
}

class Element {
 Int32 Value { get; }
 Row Row { get; }
 Column Column { get; }
}

e nel mapping uso un @cascade che sia il più "comprensivo" possibile. Tenendo sue sessioni aperte in contemporanea, avevo eccezioni di tipo "Illegal attempt to associate a collection with two open sessions", quindi necessariamente devo far "viaggiare" gli oggetti da un thread all'altro dopo averli "staccati" dalla sessione (o avere terminato la stessa). Il mio problema è che devo *necessariamente* caricare sempre tutta la porzione di grafo (a meno di non starci mooooolto attento). In tal senso, esiste qualche best practice che ancora mi sfugge? ;-)

 

Ciao e grazie,

petrux

--

 

Top 10 Partecipanti
Maschio
Post 243
Punteggio 3.383

La situazione è questa, se tieni la sessione aperta, puoi usufruire del lazy load, ma chiaramente non da più thread, altrimenti ti porti dietro problemi. La soluzione di usare dto, è quella che risolve perchè ti fa caricare subito tutta la parte di grafo che ti serve, in maniera diciamo implicita. Per la thread safety comunque se devi passare oggetti da un thread ad un altro non puoi fare altrimenti.

Io la cosa che faccio è questa, carico dei dto da dei servizi, quindi sono oggetti non mappati, elaboro con tutti i thread che voglio, e poi alla fine salvo tutto il risultato richiamando un servizio e ripassando i dto aggiornati ed i nuovi dto, il servizio riapre la sessione e riaggiorna tutto in un singolo thread e non ho problemi.

alk.

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 268
Punteggio 4.907

Ciao Alk, 

 

non so se essere contento ma, a grandi linee, è lo stesso sistema che ho usato io. :-)

(anche se i Dto si chiamano descriptor).

 

Ciao e grazie,

Giulio

-- 

Pagina 1 di 1 (5 elementi) | RSS
Powered by Community Server (Commercial Edition), by Telligent Systems