Friday, February 15, 2008

 

Are reads and writes of reference type variables atomic?

This question came up at work today:

If you have a class that has a getter and setter for a property that is a reference type (such as a user defined class) and that property will be accessed by multiple threads, do you have to protect access to getting or setting the underlying member variable?

Bascially, the other developer was asking if he had to do the following:



class MyClass1
{
}

class MyClass2
{
object _syncLock = new object();

MyClass1 _myClass1;

MyClass1 Value
{
get
{
lock(_syncLock)
{
return _myClass1;
}
}
set
{
lock(_syncLock)
{
_myClass1 = value;
}
}
}
}

 

My answer was "I never do". But, I soon as a paniced feeling as I started to second-guess myself. Do I need to do that? Had I been doing it wrong in everything .NET app I have ever written?

After thinking about it for awhile, I came to conclusion that setting and getting a reference variable has to be an atomic operation.



object a = new object();
object b;
b = a; // This line must be atomic!

 

If it was not, I reasoned, then the lock() statement we all love could not possibly be used to protect access to anything because the object referred to by the reference variable that is passed into lock could be changed by another thread as it was in-process of being assigned as the lock object for the Monitor.

After to coming to this conclusion, I went to ECMA-334 to see if it had a concrete statement of what I had just determined must be. Sure enough, section 12.5 Atomicity of Variable References states:

Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.

Ok, case closed. But this also implies something else. This means that there is an upper limit of the number of objects that can exist in .NET at any one time. Assuming that each instance of an object is assigned a unique "reference" number, that number is probably 32-bit since that is the largest number of bits that can natively be copied atomically on a 32-bit system. This means that the absolute maximum number of objects that can exist in .NET at any point in time is 2^32 = 4,294,967,296. Assuming that each of those objects took only 4-bytes to store its unique 32-bit number and nothing else, that would be 2^32 * 4 = 17,179,869,184 bytes (16GB) of memory to create every possible instance of simple objects, so I don't think we're going to run out anytime soon. Although, I can recall when 640K was considered to be 10x the amount of RAM a PC would ever need.

Comments: Post a Comment





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]