Hibernate Books

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: NHibernate and multiple threads: how?
PostPosted: Wed Jan 09, 2008 8:00 am 
Beginner
Beginner

Joined: Thu Oct 26, 2006 4:45 am
Posts: 39
Hi,

I was trying to use the same NHibernate session in multiple threads but that results in all kinds of errors like ADO Exceptions.

Is there a tutorial somewhere that explains me how to use multiple threads with NHibernate? That is: being able to read and write with NHibernate using multiple threads?

Thanks in advance,


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 5:39 pm 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
As long as you don't span a Session across threads, you should be fine. However, at that point you may begin to have problems with data resource contention between sessions. Even if they aren't using the same rows, lock escalation (in SQL Server, anyway) can cause blocking and deadlock issues.

In my case, I have found that even when using multiple threads, it is a good idea to put data access code inside lock () blocks and serialize the access between threads, which pretty much eliminates the benefit of multi-threaded DB access. (My main impetus was to use it for parallel access to a different resource anyway.)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 5:52 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
If you don't need to share a session across the threads (i.e. you don't want to share any state, and you're not dealing with the same objects across threads) you might consider opening an ISession per thread (functionality which is easy to hide from the consuming code).

As you've found, ADO doesn't like this kind of thing, and the operations in ISession specifically are not thread safe, so I'm not sure it's a good idea to try to do what you're doing.

Depending on how you access your ISession(s) you can hide the functionality using a dictionary with the key as thread ids and the values as ISessions - this way you know that a single ISession will never be simultaneously accessed by two threads.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 10, 2008 4:28 am 
Beginner
Beginner

Joined: Thu Oct 26, 2006 4:45 am
Posts: 39
For starters, I only want to be able to READ from the database in multiple threads.

I tried that by using the same session, which fails.

Below is my test code. It is important to know that MyContext is a singleton class which holds one NHibernate Session, and the LocationRepository.GetLocation() method is using MyContext to access an Isession and executing some NHibernate criteria to get information from the database. (not shown below)

Code:

        public void NHibernateThreadTest()
        {

            Thread[] threads = new Thread[10];

            ThreadTestUnsafe r = new ThreadTestUnsafe();
         
  // Create the threads
            for (int i = 0; i < 10; i++)
            {
                threads[i] = new Thread(r.ThreadMethod);
                ParameterizedThreadStart operation = new ParameterizedThreadStart(r.ThreadMethod);
                threads[i] = new Thread(operation);
            }

            for (int i = 0; i < 10; i++)
            {
                threads[i].Start(MyContext.Current);
            }

            // wait till all threads end.
            for (int i = 0; i < 10; i++)
            {
                threads[i].Join();
            }

        }

class ThreadTestUnsafe
    {
        private int sharedValue = 0;

        protected LocalDataStoreSlot myContextSlot;

        /// <summary>
        /// ThreadCode gets the specified context.
        /// </summary>
        /// <param name="context">The context.</param>
        internal void ThreadMethod(Object context)
        {
            // Apply the saved context to the current thread
            myContextSlot = Thread.GetNamedDataSlot("MyContext");
            Thread.SetData(myContextSlot, context as MyContext);
           
            for (int i = 0; i < 10; i++)
            {
                // Race condition??
                Location location = LocationRepository.GetLocation(188);
                NUnitHelper.WriteTraceLine(location.Name);
                Thread.Sleep(10);
               

            }
        }
    }



The important thing is that this fails. If I understand correctly, I should make sure that I will be using different NHibernate Sessions for different threads? I will not be writing but only reading from the database, it should than work?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 10, 2008 2:49 pm 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
hace_x wrote:
I tried that by using the same session, which fails.


And is pretty much guarateed to fail: see http://www.hibernate.org/hib_docs/nhibe ... ns-threads

Quote:
If I understand correctly, I should make sure that I will be using different NHibernate Sessions for different threads? I will not be writing but only reading from the database, it should than work?


Yes and yes. With reading only, you shouldn't have any deadlock issues at all, but if you do, just drop the isolation level down a notch.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 11, 2008 4:37 am 
Beginner
Beginner

Joined: Thu Oct 26, 2006 4:45 am
Posts: 39
Marcal, thank you.

I have another question though, about using an already loaded object.

When starting a new thread, a new session is now opened.

This is working for different scenario's but not for all.

The scenario this is not working for is when an object has a collection that is lazy-loaded. To clarify:

Some object A (which has a bag that will be lazily loaded later) is loaded in the main thread.

Two other threads are loaded. Within these new threads (and new sessions) the object A.BCollection is called. this than gives an exception because (I presume) the object A is not yet 'known" in this new session.

This was solved when I added the following line in the threads:

threadSession.Refresh(A);

However I also read documentation references that not ISession.Refresh but ISession.Lock(A) should be used. Where can I find a good example?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 14, 2008 1:11 pm 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
Good question. The documentation implies that Refresh(object o) should be used when o is already in the session, but needs to be re-read from the DB to update any properties that were set by triggers or other mechanisms in the DB.

According to section 6.5 ( http://www.hibernate.org/hib_docs/nhibe ... tions-lazy ) you should use Update() or Lock(). Refresh() will force a select, whereas Lock() will not.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.