Is there a race condition in this common pattern used to prevent NullReferenceException?

时光总嘲笑我的痴心妄想 提交于 2019-11-29 01:07:57
Douglas

In CLR via C# (pp. 264–265), Jeffrey Richter discusses this specific problem, and acknowledges that it is possible for the local variable to be swapped out:

[T]his code could be optimized by the compiler to remove the local […] variable entirely. If this happens, this version of the code is identical to the [version that references the event/callback directly twice], so a NullReferenceException is still possible.

Richter suggests the use of Interlocked.CompareExchange<T> to definitively resolve this issue:

public void DoCallback() 
{
    Action local = Interlocked.CompareExchange(ref _Callback, null, null);
    if (local != null)
        local();
}

However, Richter acknowledges that Microsoft’s just-in-time (JIT) compiler does not optimize away the local variable; and, although this could, in theory, change, it almost certainly never will because it would cause too many applications to break as a result.

This question has already been asked and answered at length in “Allowed C# Compiler optimization on local variables and refetching value from memory”. Make sure to read the answer by xanatox and the “Understand the Impact of Low-Lock Techniques in Multithreaded Apps” article it cites. Since you asked specifically about Mono, you should pay attention to referenced “[Mono-dev] Memory Model?” mailing list message:

Right now we provide loose semantics close to ecma backed by the architecture you're running.

This code will not throw a null reference exception. This one is thread safe:

public void DoCallback() {
    Action local;
    local = Callback;
    if (local == null)
        local = new Action(() => { });
    local();
}

The reason this one is thread-safe, and can-not throw a NullReferenceException on Callback, is it's copying to a local variable before doing it's null check / call. Even if the original Callback was set to null after the null check, the local variable will still be valid.

However the following is a different story:

public void DoCallbackIfElse() {
    if (null != Callback) Callback();
    else new Action(() => { })();
}

In this one it's looking at a public variable, Callback can be changed to null AFTER the if (null != Callback) which would throw an exception on Callback();

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!