views:

49

answers:

4

Is this piece of code where I lock a part of the function correct? Or can it have use drawbacks when multiple sessions ask concurrently for the same Exam?

Purpose is that client that first asks for the Exam will assemble it, all next clients will get the cached version.

public Exam GetExamByExamDto(ExamDTO examDto, int languageId)
{
    Log.Warn("GetExamByExamDto");
    lock (LockString)
    {
        if (!ContainsExam(examDto.id, languageId))
        {
            Log.Warn("Assembling ExamDto");
            var examAssembler = new ExamAssembler();
            var exam = examAssembler.createExam(examDto);

            if (AddToCache(exam))
            {
                _examDictionary.Add(examDto.id + "_" + languageId, exam);
            }
            Log.Warn("Returning non cached ExamDto");
            return exam;
        }
    }
    Log.Warn("Returning cached ExamDto");
    return _examDictionary[examDto.id + "_" + languageId];
}

I have a feeling that this isn't the way to do it.

+5  A: 

Never lock on strings - they are immutable and interned, so when trying to access the same string elsewhere you may end up locking your whole application.

Just use a new object as your lock:

private readonly object Padlock = new object();

See this blog post by Tess Ferrandez.

Oded
+2  A: 

The LockString variable, is it really a string? You shouldn't lock on a string, as you may end up with problems to do with string interning that will cause multiple locks to lock up.

The other thing is that there seems to be a lot of code inside the lock, meaning that you lock for longer than you may have to. If you can, try to get as much code out of the lock as possible.

ilivewithian
+2  A: 

It looks basically OK but you should not lock on a string. Interning makes it hard to control which instance is which. Just create a separate object to lock on:

 private object padLock = new object();
Henk Holterman
+1  A: 

Also you can use double check (after the exam is cached Monitor.Lock won't be invoked at all):

public Exam GetExamByExamDto(ExamDTO examDto, int languageId)
{
    Log.Warn("GetExamByExamDto");
    if (!ContainsExam(examDto.id, languageId))
    {
        lock (LockString) // Here should be some private locking object.
        {
            if (!ContainsExam(examDto.id, languageId))
            {
                Log.Warn("Assembling ExamDto");
                var examAssembler = new ExamAssembler();
                var exam = examAssembler.createExam(examDto);

                if (AddToCache(exam))
                {
                    _examDictionary.Add(examDto.id + "_" + languageId, exam);
                }
                Log.Warn("Returning non cached ExamDto");
                return exam;
            }
        }
        Log.Warn("Returning cached ExamDto");
        return _examDictionary[examDto.id + "_" + languageId];
    }
}
Andrew Bezzub
Thx, Andrew, I'll now check performance with your adjustment.
Lieven Cardoen
Are you sure `ContainsExam` is thread-safe?
Henk Holterman
Ah, we were just looking into a problem with your code. It'll probably not be threadsafe.
Lieven Cardoen
Although, yes, it is threadsafe. Will be something else.
Lieven Cardoen
@Lieven, Still, this is optimization and I would revert to your original code unless you really have a speed problem. Double-check locking often turns out to be broken.
Henk Holterman
@Henk, could you give and example when such check could be broken?
Andrew Bezzub
Below is a link for Java, in .NET it depends on memory models etc. http://www.javaworld.com/jw-02-2001/jw-0209-double.html
Henk Holterman
Your link is too Java specific, it doesn't mean that DCL won't work in .NET application. Do you have any .NET explanations? Thanks in advance.
Andrew Bezzub
I have checked this solution in .NET and it seems to work pretty good. I have 300 calls to the code at exact the same time, so I guess some will have to wait on the lock, but others will come in when the Exam is already cached, which is good. But I need to run some other tests to make sure I really is a performance improvement.
Lieven Cardoen