Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is locking instance or member based

I have a question about locking in c#. Does c# lock an instance of an object, or the member.

If i have the following code:

lock(testVar)
{
    testVar = testVar.Where(Item => Item.Value == 1).ToList();
    //... do some more stuff
}

Does c# keep the lock, even i set testVar to a new value?

like image 863
BendEg Avatar asked Dec 15 '22 17:12

BendEg


2 Answers

All C# objects inherit from System.Object, which itself always contains 4 bytes dedicated to be used when you use the syntactic sugar for lock. That's called a SyncBlock object.

When you create a new object using new, in your case, ToList which generated a new reference to a List<T>, you're actually overriding the old reference, which invalidates your lock. That means that now multiple threads could possibly be inside your lock. The compiler will transform your code into a try-finally block with an extra local variable, to avoid you from shooting your leg.

That is why the best practice is to define a dedicated private readonly variable which will act as a sync root object, instead of using a class member. That way, your intentions are clear to anyone reading your code.

Edit:

There is a nice article on MSDN which describes the objects structure in memory:

SyncTableEntry also stores a pointer to SyncBlock that contains useful information, but is rarely needed by all instances of an object. This information includes the object's lock, its hash code, any thunking data, and its AppDomain index. For most object instances, there will be no storage allocated for the actual SyncBlock and the syncblk number will be zero. This will change when the execution thread hits statements like lock(obj) or obj.GetHashCode.

Object in memory representation

like image 175
Yuval Itzchakov Avatar answered Dec 30 '22 05:12

Yuval Itzchakov


It locks on the object that the expression (testVar) resolves to. This means that your code does have a thread race, because once the list is reassigned, other concurrent threads could be locking on the new instance.

A good rule of thumb: only ever lock on a readonly field. testVar clearly isn't... but it could be, especially if you use RemoveAll to change the existing list instead of creating a new one. This of course depends on all access to the list happening inside the lock.

Frankly, though, most code doesn't need to be thread-safe. If code does need to be thread safe, the supported use scenarios must be clearly understood by the implementer.

like image 28
Marc Gravell Avatar answered Dec 30 '22 05:12

Marc Gravell