-->

lock vs Interlocked.Exchange

2019-07-08 20:44发布

问题:

I have an application which constantly (+-100ms) reads orders from a PLC and then puts them in a model which then gets read by multiple clients. For this im using the lock statement.

Order Reading thread :

lock (model) {
//update object
}

Clients Reading :

lock (model) {
//serialize object to json string
}
send over tcp stream to client.

But i could also use for the update :

Interlocked.ExChange(oldObj, newObj)

I don't want my clients to have to wait for a lock that is happening in the Order Reading thread. And i definitly dont want client to block my Order Reading thread.

Am i better off using the Interlocked ?

Thanks for the advice!

回答1:

Yes, you are better off using Interlocked as it's more efficient since it's mostly translated into a single atomic operation.

However, if you don't mind the clients still reading the old object for a bit you can even do without the Interlocked and just set a new instance.

The client that happen to get the new instance will get the updated data and those that don't will get it in one of the next checks.



回答2:

If your single producer is creating a brand new model, and assigning it to a shared field, then yes, Interlocked.Exchange is the way to go here.

If your producer does not need to know what the old model was, then you could use Volatile.Write(ref _sharedObj, newObj) instead to keep it simpler.

Just be aware of 3 things:

  1. Use Volatile.Read to read your shared object.
  2. The consumers should read the shared state once per unit of work

    //incorrect - _sharedObj is not guaranteed to be the same in both reads
    var x = CalculateX(_sharedObj);
    var y = CalculateY(_sharedObj);
    
    //correct
    var obj = Volatile.Read(ref _sharedObj);
    var x = CalculateX(obj);
    var y = CalculateY(obj);
    
  3. The consumers will sometimes be using a slightly outdated model. So be sure that using a slightly outdated object doesn't cause you any trouble

    var obj = _sharedObj;
    // If _sharedObj is exchanged here, then `obj` will be outdated
    var x = CalculateX(obj);
    var y = CalculateY(obj);