Multithreading – Strategies to Handle or Avoid Race Conditions

databasemultithreading

I'm working on a .NET application where I off-load my DB work (save operations) to a separate thread (inspired by how NodeJS handles IO operations). A save operation comes down to this (using Entity Framework):

public void Save(Customer customer)
{
    Task.Factory.StartNew(() =>
    {
        using (var entities = new MyEntities())
        {
            var persistedCustomer 
                = entities.Customers.FirstOrDefault(x => x.Number == customer.Number);

            if (persistedCustomer == null)
            {
                persistedCustomer = new DBCustomer();
                // set properties
                entities.Customers.AddObject(persistedCustomer);
            }
            else
            {
                // set properties
            }

            entities.SaveChanges();
        }
    });
}

So, in essence:

  • check if entity already exists in the DB
  • if not, create a new one
  • if it does already exist, update the existing one

Where this sometimes breaks down is when two calls to the Save method are made at the same time. Both threads see the entity doesn't exist yet and they want to do an insert. A standard race condition.

How would you go about fixing this? Any experience with this?

I could implement a retry mechanism, but was wondering if there are other options. A simple retry could of course overwrite certain properties that the previous save had updated.

Best Answer

Problems like this are often solved with queues.

If you are saving same customer, you insert it into one ConcurrentQueue<T>. Then there is one thread that reads the queue sequentially and does the update on each item read. Only place where the synchronization between threads occurs is in insertion to the queue and that can be done both fast and without risk of deadlocks.