4

According to this reference assignment is atomic so why is Interlocked.Exchange(ref Object, Object) needed? Reference assignment is guaranteed to be atomic on all .NET platforms. Will this code is atomic,

public static List<MyType> _items;

public static List<MyType> Items
{
    get
    {
        if (_items== null)
        {
            _items= JsonConvert.DeserializeObject<List<MyType>>(ConfigurationManager.AppSettings["Items"]);
        }
        return _items;
    }
}

I know there might be multiple object as given here. But will it Items will be atomic(I mean it will be either null or List and not in middle)?

iehrlich
  • 3,572
  • 4
  • 34
  • 43
Imran Qadir Baksh - Baloch
  • 32,612
  • 68
  • 179
  • 322

1 Answers1

2

No, this code is not atomic - if Items is accessed from multiple threads in parallel, _items may actually get created more than once and different callers may receive a different value.

This code needs locking because it first performs a read, a branch and a write (after an expensive deserialization call). The read and the write by themselves are atomic but - without a lock - there's nothing to prevent the system to switch to another thread between the read and the write.

In pseudo(ish) code, this is what may happen:

if (_items==null)
    // Thread may be interrupted here.
{
    // Thread may be interrupted inside this call in many places,
    // so another thread may enter the body of the if() and
    // call this same function again.
    var s = ConfigurationManager.AppSettings.get_Item("Items");

    // Thread may be interrupted inside this call in many places,
    // so another thread may enter the body of the if() and
    // call this same function again.
    var i = JsonConvert.DeserializeObject(s);

    // Thread may be interrupted here.
    _items = i;
}

// Thread may be interrupted here.
return (_items);

This shows you that without locking it's possible for multiple callers to get a different instance of the Items list.

You should look into using Lazy<T> which will make this sort of initialization a lot simpler and safe.

When should I use Lazy<T>?

Also, keep in mind that List<T> itself is not thread-safe - you may want to use a different type (like ConcurrentDictionary<T1, T2> or ReadOnlyCollection<T>) or you may need to use locking around all operations against this list.

Rob, in the comments, pointed out that the question may be about whether a given assignment is atomic - a single assignment (that is a single write) of a reference is guaranteed to be atomic but that doesn't make this code safe because there's more than a single assignment here.

Community
  • 1
  • 1
xxbbcc
  • 16,930
  • 5
  • 50
  • 83
  • 1
    This is correct, but I *believe* OP is asking whether or not the *assignment* is atomic. That is, there will never be a 'half constructed' object assigned to `_items`. – Rob May 17 '17 at 06:48
  • @Rob You could be right - let me update the answer to account for that. Thank you. – xxbbcc May 17 '17 at 06:49