Allowed C# Compiler optimization on local variables and refetching value from memory Allowed C# Compiler optimization on local variables and refetching value from memory multithreading multithreading

Allowed C# Compiler optimization on local variables and refetching value from memory


I will say (partially) the opposite of mgronber :-) Aaaah... In the end I'm saying the same things... Only that I'm quoting an article :-( I'll give him a +1.

It's a LEGAL optimization under the ECMA specifications, but it's an illegal optimization under the .NET >= 2.0 "specifications".

From the Understand the Impact of Low-Lock Techniques in Multithreaded Apps

Read here Strong Model 2: .NET Framework 2.0

Point 2:

Reads and writes cannot be introduced.

The explanation below:

The model does not allow reads to be introduced, however, because this would imply refetching a value from memory, and in low-lock code memory could be changing.

BUT note under in the same page, under Technique 1: Avoiding Locks on Some Reads

In systems using the ECMA model, there is an additional subtlety. Even if only one memory location is fetched into a local variable and that local is used multiple times, each use might have a different value! This is because the ECMA model lets the compiler eliminate the local variable and re-fetch the location on each use. If updates are happening concurrently, each fetch could have a different value. This behavior can be suppressed with volatile declarations, but the issue is easy to miss.

If you are writing under Mono, you should be advised that at least until 2008 it was working on the ECMA memory model (or so they wrote in their mailing list)


It is legal optimization according to the memory model defined in the ECMA specification. If the _action were volatile, memory model would guarantee that the value is read only once and so this optimization could not happen.

However, I think that current Microsoft's CLR implementations do not optimize local variables away.


With C# 7, you should write the example as follows, and in fact the IDE will suggest it as a 'simplification' for you. The compiler will generate code that uses a temporary local to only read the location of _action from main memory a single time (regardless of it being null or not), and this helps prevent the common race shown the OP's second example, i.e., where _action is accessed twice, and can be set to null by another thread inbetween.

class Example{    private Action _action;    private void InvokeAction()    {        this._action?.Invoke();    }}