简体   繁体   中英

Is locking single session in repository thread safe? (NHibernate)

I read many posts saying multithreaded applications must use a separate session per thread. Perhaps I don't understand how the locking works, but if I put a lock on the session in all repository methods, would that not make a single static session thread safe?

like:

public void SaveOrUpdate(T instance)
{
    if (instance == null) return;
    lock (_session)
        using (ITransaction transaction = _session.BeginTransaction())
        {
            lock (instance)
            {
                _session.SaveOrUpdate(instance);
                transaction.Commit();
            }
        }
}

EDIT:

Please consider the context/type of applications I'm writing:

Not multi-user, not typical user-interaction, but a self-running robot reacting to remote events like financial data and order-updates, performing tasks and saves based on that. Intermittently this can create clusters of up to 10 saves per second. Typically it's the same object graph that needs to be saved every time. Also, on startup, the program does load the full database into an entity-object-graph. So it basically just reads once, then performs SaveOrUpdates as it runs.

Given that the application is typically editing the same object graph, perhaps it would make more sense to have a single thread dedicated to applying these edits to the object graph and then saving them to the database, or perhaps a pool of threads servicing a common queue of edits, where each thread has it's own (dedicated) session that it does not need to lock. Look up producer/consumer queues (to start, look here ).

Something like this:

[Producer Threads]
Edit Event -\                [Database Servicer Thread]
Edit Event ------> Queue -> Dequeue and Apply to Session -> Database
Edit Event -/ 

I'd imagine that a BlockingCollection<Action<Session>> would be a good starting point for such an implementation.

Here's a rough example (note this is obviously untested):

// Assuming you have a work queue defined as 
public static BlockingCollection<Action<Session>> myWorkQueue = new BlockingCollection<Action<Session>>();

// and your eventargs looks something like this
public class MyObjectUpdatedEventArgs : EventArgs {
    public MyObject MyObject { get; set; }
}

// And one of your event handlers
public MyObjectWasChangedEventHandler(object sender, MyObjectUpdatedEventArgs e) {
    myWorkQueue.Add(s=>SaveOrUpdate(e.MyObject));
}

// Then a thread in a constant loop processing these items could work:
public void ProcessWorkQueue() {
    var mySession = mySessionFactory.CreateSession();
    while (true) {
        var nextWork = myWorkQueue.Take();
        nextWork(mySession);
    }
}

// And to run the above:
var dbUpdateThread = new Thread(ProcessWorkQueue);
dbUpdateThread.IsBackground = true;
dbUpdateThread.Start();

At least two disadvantages are:

  1. You are reducing the performance significantly. Having this on a busy web server is like having a crowd outside a cinema but letting people go in through a person-wide entrance.

  2. A session has its internal identity map (cache). A single session per application means that the memory consumption grows as users access different data from the database. Ultimately you can even end up with the whole database in the memory which of course would just not work. This requires then calling a method to drop the 1st level cache from time to time. However, there is no good moment to drop the cache. You just can't drop in at the beginning of a request because other concurrent sessions could suffer from this.

I am sure people will add other disadvantages.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM