views:

115

answers:

3

Hi, In my multithreading application I am using some variables that can be altered by many instances in the same time. It is weird but it has worked fine without any problem..but of course I need to make it thread-safe. I am just beginning with locks so I would appretiate your advice:

When client connects, class Client is created where each Client has its own "A" variable.

Sometimes, Client calls method like that:

Client selectedClient SelectOtherClientClassByID(sentID);

selectedClient.A=5;

No problems until now with that even when 5 classes were doing at the same time (threadpool), but I was thinking what about adding locks to A properties?

Like: A{ get{ return mA; } set{ use lock here for settting A to some value

} }

Would it be OK?

+7  A: 

You need to use locks in BOTH get and set. This lock must be the same object. For example:

private object mylock = new object();

public int A {

  get {
    int result;
    lock(mylock) {
    result = mA; 
    }
    return result;
  } 

  set { 
     lock(mylock) { 
        mA = value; 
     }
  }
}
_NT
Thank you...btw for reading its because it could changed during the reading by other class?
Petr
It is not a good idea to lock on this, as it is accessible from outside of the type.
Brian Rasmussen
It could change while you're setting. The lock acts as a barrier, preventing any other action until the lock owner releases the lock.
_NT
@Brian, I agree. Made the change on my code. Was simply demonstrating.
_NT
Sorry I was on the meeting, _NT please, how did your code look before you change, I mean what was the problem Brian mentioned? I like to learn as much as I can :)
Petr
I did lock(this) to lock the object I'm currently on.
_NT
+1  A: 

It's very rare when all you need is just set a single property. More often selectedClient.A = 5 will be a part of a much bigger logical operation, which involves several assignments/evaluations/etc. During that whole operation you'd rather prefer selectedClient to be in a consistent state and not to introduce deadlocks/race conditions. Therefore, it will be much better to expose SyncRoot property in your Client class and lock on that from the calling code:

Client selectedClient = GetClient(...);

lock(selectedClient.SyncRoot)
{
    selectedClient.A = 5;
    selectedClient.B = selectedClient.A * 54;
}
Anton Gogolev
I have no other variables sharing than one list and those int. Mainly, I know nothing about SyncRoot yet :)
Petr
SyncRoot is not recommended: See http://blogs.msdn.com/brada/archive/2003/09/28/50391.aspx
_NT
@_NT: Quoting from the blog post: "Rest assured we will not make the same mistake as we build the generic versions of these collections." And here we are, 6 years later, with List<T>.SyncRoot without any discouraging notes in the MSDN: http://msdn.microsoft.com/en-us/library/bb356596.aspx
Anton Gogolev
MSDN is hardly the best resource for correct programming! SyncRoot, is a public lock and a disaster waiting to happen (according to CLR via C# by Jeffrey Richter http://www.amazon.co.uk/gp/reader/0735621632/ref=sib_dp_pt#reader-link)
_NT
A: 

Locking access to properties inside of accessors may lead to bogus results. For the example, look at the following code:

class C {
    private object mylock = new object();

    public int A {

      get {
        int result;
        lock(mylock) {
        result = mA; 
        }
        return result;
      } 

      set { 
         lock(mylock) { 
            mA = value; 
         }
      }
    }
}
C obj = new C;
C.A++;

(yes, I've copied it from the first answer) There is a race condition here! Operation "C.A++" actually requires two separate accesses to A, one to get the value and the other to set the updated value. Nothing ensures that these two accesses will be carried out as together without context switch between them. Classical scenario for race condition!

So, beware! It's not a good idea to put locks inside accessors, locks should be explicitly obtained, like the previous answer suggests (though it doesn't have to be with SyncRoots, any object will do)

ilial
Also you mean manually add lock object to all calls in the code? I guess there are at least 20 readings and 15 writings iny my code so far.
Petr
To do that you expand your lock to cover the entire critical region. You don't do A++.
_NT
NT: I dont understand you well probably.I have around 15 methods with different operations with that variable on different instance. Im not sure how you meant expanding the lock.So the properties lock is not good if I read correctly all above
Petr
@Petr: I directed my previous comment to ilial. As for your comment, you would need to establish whether these have a common synchonisation object. In that case you should use a single lock. If you're new to all this have a look at this interesting link: http://www.codeproject.com/KB/threads/ThreadingDotNet3.aspx#CriticalSections
_NT