Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent two threads entering a code block with the same value

Say I have this function (assume I'm accessing Cache in a threadsafe way):

object GetCachedValue(string id)
{
    if (!Cache.ContainsKey(id))
    {
         //long running operation to fetch the value for id
         object value = GetTheValueForId(id);
         Cache.Add(id, value);
    }     
    return Cache[id];
}

I want to prevent two threads from running the "long running operation" at the same time for the same value. Obviously I can wrap the whole thing in a lock(), but then the whole function would block regardless of value and I want two threads to be able to perform the long running operation as long as they're looking for different id's.

Is there a built-in locking mechanism to lock based on a value so one thread can block while the other thread completes the long running operation so I don't need to do it twice (or N times)? Ideally as long as the long running operation is being performed in one thread, no other thread should be able to do it for the same id value.

I could roll my own by putting the id's in a HashSet and then removing them once the operation completes, but that seems like a hack.

like image 663
powlette Avatar asked Dec 28 '12 17:12

powlette


1 Answers

I would use Lazy<T> here. Below code will lock the cache, put the Lazy into the cache and return immediately. Long-running-operation will be executed once in a thread safe manner.

new Thread(() => Console.WriteLine("1-" + GetCachedValue("1").Value)).Start();
new Thread(() => Console.WriteLine("2-" + GetCachedValue("1").Value)).Start();

Lazy<object> GetCachedValue(string id)
{
    lock (Cache)
    {
        if (!Cache.ContainsKey(id))
        {
            Lazy<object> lazy = new Lazy<object>(() =>
                {
                    Console.WriteLine("**Long Running Job**");
                    Thread.Sleep(3000);
                    return int.Parse(id);
                }, 
                true);

            Cache.Add(id, lazy);
            Console.WriteLine("added to cache");
        }
        return Cache[id];
    }
}
like image 101
L.B Avatar answered Oct 03 '22 23:10

L.B