Quick question. Is below assignment atomic:
object [] table = new object[10]
...
table[3] = 10; //is it atomic ?
...
Quick question. Is below assignment atomic:
object [] table = new object[10]
...
table[3] = 10; //is it atomic ?
...
Yes, the assignment is atomic because object is a reference type.
As mentioned in the below quote from MSDN writes are atomic for most (not all) of buil-in value types (mentioned below) and reference types. The CLI guarantees that reads and writes of variables of value types that are the size (or smaller) of the processor's natural pointer size are atomic; if you are running C# code on a 64 bit operating system in a 64 bit version of the CLR then reads and writes of 64 bit doubles and long integers are also guaranteed to be atomic. The C# language does not guarantee that, but the runtime spec does.
what MSDN says
Reads and writes of the following data types are 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 are also atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, are not guaranteed to 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.
A very detailed answer by none other than Eric himself can be seen here
I am assuming OP means 'thread safe' when talking about atomicity.
A write operation is not atomic only if it is possible that another thread can read partially written value.
If object [] table
is local to a method, each thread will get their own table and hence any operation on the table will be atomic.
Going forward I am assuming that table
is shared across threads.
OP has defined table as an array of object
. Hence table[3] = 10
involves boxing.
Even though table[3] = 10
represents a chain of instructions, this operation is atomic because this will eventually be writing the 'address' of the boxed instance (Current CLR implementation do represent object reference using memory address) and addresses are of the natural word size of the machine (i.e. 32 bit on 32 bit machines and 64 bit on 64 bit machines). Please note that even though above explanation is based on current CLR implementation, atomicity of reference writing is guaranteed by specs. The operation of boxing and the boxed instance itself are local to the thread and hence there is no way that another thread can interfere in that.
Going by same reasoning even if a value exceeding the word size (for e.g. Decimal
) was being written, the operation would have been atomic (due to boxing).
It must be noted here that the above argument holds only if the value being written has already been obtained in a thread safe manner.
Had no boxing been involved, then the usual rules of word size writes (or sizes lesser than the word size, owing to memory alignment) being atomic holds.
I seriously doubt that the entire term table[3] = 10;
is atomic. While a single read or write to some data types are atomic array acces is most likely not.
The IL code for your example is
IL_0001: ldc.i4.s 0A
IL_0003: newarr System.Object
IL_0008: stloc.0 // table
IL_0009: ldloc.0 // table
IL_000A: ldc.i4.3
IL_000B: ldc.i4.s 0A
IL_000D: box System.Int32
IL_0012: stelem.ref
You can see that the index is loaded at IL_000A, then a boxing of the int occurs at IL_000D and finally the boxed value is stored at IL_0012.
Each indidivual of these instructions might be atomic, but that doesn't mean that the chain of instructions that make table[3] = 10;
is.