views:

40

answers:

2

I have a multi-threaded application. Threads use ABC.Connector. I want that only one thread at a time have access to a Connector property.

class ABC
{
    /// <summary>
    /// Synchronization object.
    /// </summary>
    static object _syncObject = new object();

    static DataAccess _connector;
    /// <summary>
    /// Global object for data access.
    /// </summary>
    public static DataAccess Connector
    {
        get
        {
            lock (_syncObject)
            {
                return _connector.CreateCopy(); // get a copy of original _connector
            }
        }
        set
        {
            lock (_syncObject)
            {
                _connector = value;
            }
        }
    }
}

Is it correct ?

+1  A: 

Well, that will certainly make getting and setting the Connector property thread-safe (although I'd make _syncObject read-only). However, it doesn't make DataAccess thread-safe... the mutex will only apply while threads are getting and setting the property.

In other words, if two threads both do:

ABC.DataAccess.DoSomeLongRunningOperation();

then DoSomeLongRunningOperation() will still be run concurrently by the two threads. If that operation isn't thread-safe, it's still going to be a problem.

If you only want one thread at a time to use the DataAccess then you could write:

public static void UseConnector(Action<DataAccess> action)
{
    lock (_syncObject)
    {
        action(_connector);
    }
}

Then if two threads both do:

ABC.UseConnector(access => access.DoLongRunningOperation());

then DoLongRunningOperation() will only run in one thread at a time. You've still got the issue that misbehaving clients could write:

DataAccess naughty = null;
ABC.UseConnector(access => naughty = access);
// Haha! I've circumvented your thread safety!
naughty.DoLongRunningOperation();

... but hopefully that isn't an issue for you.

Jon Skeet
@Jon, thanks for a detailed answer, Jon!
nik
@nik - not just detailed, but crucially more correct than others I see at present. There is limited value in merely obtaining a reference to or copy of a non-locked object in a threadsafe way. All access to the new object via any reference must also be properly locked to ensure thread safety. That is usually the hard part. In this case you have to be 100% sure that CreateCopy _totally_ decouples the new instance from any referenced object in the old.
Steve Townsend
+1  A: 

Yes, this is generally correct. But take into account, that after Connector get returns the _connector reference, access to it is unsynchronized.

Alex Farber
@Alex, thanks, Alex. Our system administrators disable pictures so I don't see if I marked your answer is useful (green mark). Did I?
nik
You can mark any number of answers as useful by pressing Up sign, and accept only one of them by pressing the v sign. When you mark answer as useful, its counter is incremented. Now my answer has counter "1". I have no idea how this site looks without images, I guess a bit strange :(
Alex Farber
@Alex, it looks terrible. I don't even see Up sign :( ohhh, but I can see tooltips!
nik
I hope our sysadmin doesn't read this, disabling pictures may look like a good idea for these guys :) But at least you have tooltips, this is fine!
Alex Farber
Well, actually our sysadmin is illiterate...
Alex Farber
@Alex, ours sysadmins are stupid too :)
nik