You learn something new every day. As K Scott Allen points out,
read or write operations to CLI datatypes larger than the native word size (32 bit on your basic PC) are not guaranteed to be atomic.
So, in the course of:
private long i = 0x00000000FFFFFFFF;
public void Increment()
{
i++;
}
it's possible for two threads to interact as follows [edit - with hexadecimals done right this time. Idiot brain was - for some reason - only doing binary earlier.]:
| i | Thread 1 | Thread 2 |
| 0x00000000FFFFFFFF | | reads first four bytes of i (0x00000000) |
| 0x00000000FFFFFFFF | reads first four bytes of i (0x00000000) | |
| 0x00000000FFFFFFFF | reads second four bytes of i (0xFFFFFFFF) | |
| 0x00000000FFFFFFFF | increments 0x00000000FFFFFFFF (0x0000000100000000) | |
| 0x00000001FFFFFFFF | writes first four bytes of i (0x00000001) | |
| 0x0000000100000000 | writes second four bytes of i (0x00000000) | |
| 0x0000000100000000 | | reads second four bytes of i (0x00000000) |
| 0x0000000100000000 | | increments 0x0000000000000000 (0x0000000000000001) |
| 0x0000000000000000 | | writes first four bytes to i (0x00000000) |
| 0x0000000000000001 | | writes second four bytes to i (0x00000001) |
A non-atomic read causing trouble there. A non-atomic write could be odd, too:
| i | Thread 1 | Thread 2 |
| 0x00000000FFFFFFFF | | reads first four bytes of i (0x00000000) |
| 0x00000000FFFFFFFF | | reads second four bytes of i (0xFFFFFFFF) |
| 0x00000000FFFFFFFF | | increments 0x00000000FFFFFFFF (0x0000000100000000) |
| 0x00000001FFFFFFFF | | writes first four bytes to i (0x00000001) |
| 0x00000001FFFFFFFF | reads first four bytes of i (0x00000001) | |
| 0x00000001FFFFFFFF | reads second four bytes of i (0xFFFFFFFF) | |
| 0x00000001FFFFFFFF | increments 0x00000001FFFFFFFF (0x0000000200000000) | |
| 0x00000002FFFFFFFF | writes first four bytes of i (0x00000002) | |
| 0x0000000200000000 | writes second four bytes of i (0x00000000) | |
| 0x0000000200000000 | | writes second four bytes to i (0x00000000) |
Meaning that after calling Increment() from two threads simultaneously, the result could be that i == 0x0000000000000001, or i==0x0000000200000000, rather than the desired 0x0000000100000001 - in addition to the simple increment race condition where the reads and writes on two threads are interleaved, giving 0x0000000100000000
Tricksy things, threads...