The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

Thread-safe repositories

Posted by Davy Brion on March 25th, 2008

First of all, this is not about true DDD repositories… i just needed an in-memory data container with basic storing/retrieving functionality so i figured i’d call it a Repository.

These repositories will be accessed by multiple concurrent threads so i needed to make sure that all access to the underlying dictionary is properly synchronized. But, i also want them to be highly usable and i especially wanted a thread-safe way of dynamically executing queries on them.

Here’s what i came up with:

    public class Repository<T> where T : Member

    {

        private readonly object _monitor = new object();

        private readonly Dictionary<Id, T> _members = new Dictionary<Id,T>();

 

        protected object Monitor

        {

            get { return _monitor; }

        }

 

        /// <summary>

        /// any derived classes that use this property are responsible for their own locking!

        /// (use the Monitor property for that)

        /// </summary>

        protected Dictionary<Id, T> Members

        {

            get { return _members; }

        }

 

        public virtual void Store(T member)

        {

            lock (Monitor)

            {

                Put(member);

            }

        }

 

        public virtual void StoreRange(IEnumerable<T> members)

        {

            lock (Monitor)

            {

                foreach (T member in members)

                {

                    Put(member);

                }

            }

        }

 

        public virtual void Remove(T member)

        {

            lock (Monitor)

            {

                if (Members.ContainsValue(member))

                {

                    Members.Remove(member.Id);

                }

            }

        }

 

        public virtual T Get(Id id)

        {

            lock (Monitor)

            {

                if (!Members.ContainsKey(id))

                {

                    return null;

                }

 

                return Members[id];

            }

        }

 

        public virtual T FindFirst(Func<T, bool> expression)

        {

            lock (Monitor)

            {

                return Members.Values.FirstOrDefault(expression);

            }

        }

 

        public virtual IEnumerable<T> FindAll(Func<T, bool> expression)

        {

            lock (Monitor)

            {

                return Members.Values.Where(expression).ExecuteImmediately();

            }

        }

 

        /// <summary>

        /// this method should ONLY be called when a lock on Monitor has been acquired!

        /// </summary>

        /// <param name=”member”></param>

        private void Put(T member)

        {

            // this overwrites existing entries with the same ID… it’s not a mistake ;)

            Members[member.Id] = member;

        }

    }

each access to the dictionary is properly synchronized (at least, i think so… if anyone knows of a better way to do the locking, please leave a comment) and i can still execute dynamic queries on them by passing lambda expressions to the FindFirst and FindAll methods, like this:

            User me = userReposority.FindFirst(u => u.FirstName == “Davy” && u.LastName == “Brion”);

            IEnumerable<User> usersWithNoLastName =

                userReposority.FindAll(u => string.IsNullOrEmpty(u.LastName));

2 Responses to “Thread-safe repositories”

  1. Davy Brion Says:

    received the following comment from Paul Stovell:

    Hi Davy,

    Good article. Just one comment: I’d be careful with code like this:

    public virtual T FindFirst(Func expression)
    {
    lock (Monitor)
    {
    return Members.Values.FirstOrDefault(expression);
    }
    }

    Holding a lock whilst calling into user-supplied code can be dangerous. They may call out to a function that also acquires a lock, but that lock may be held by another thread waiting for the results of another query on your repository (that shares the same monitor).

    Let me give an example:
    “Background Synchronizer” thread holds lock on Synchronizer, calls CustomerRepository.FindAll(…) [holding Synchronizer.Monitor, requesting Repository.Monitor]

    “UI” thread performs search; calls CustomerRepository.FindAll(callback). The callback makes use of the Synchronizer [holding Repository.Monitor, requesting Synchronizer.Monitor]

    What you end up with is a classic “has A, needs B” while “has B, needs A” deadlock scenario. But your code only controls one of the locks, and their code controls another. Avoiding deadlocks isn’t as simple as avoiding lock B->lock A in your own class.

    My preference is to take a snapshot of the objects to process whilst in the lock, and to process them outside of the lock:

    public virtual T FindFirst(Func expression)
    {
    List snapshot = new List();
    lock (Monitor)
    {
    snapshot.AddRange(Members.Values);
    }
    return snapshot.FirstOrDefault(expression)
    }

    Though it may hit problems with a big collection. A more efficient approach is a snapshot invalidation system where you keep your snapshot around until it is invalidated rather than building it every time.

    Alternatively in this case you may be able to avoid the lock completely – though sometimes it’s unavoidable (saving things, for example).

    It’s also why I sometimes prefer to use Monitor.TryEnter(lock, timeout) – you avoid deadlocks but suffer/have to plan for exceptions instead. At least you don’t run the risk of tying up every thread in the threadpool.

  2. The Inquisitive Coder » Blog Archive » Easy non-blocking locking Says:

    [...] may remember my post about thread-safe repositories from a while ago. In the code from that post, i use the lock keyword to make sure instances of the [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>