锁定 vs Interlocked.Exchange

3

我有一个应用程序,它不断(+-100毫秒)从PLC中读取订单,然后将它们放入一个模型中,多个客户端会读取这个模型。 为此,我使用了锁语句。

订单读取线程:

lock (model) {
//update object
}

客户端读取:

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

但我也可以用于更新:

Interlocked.ExChange(oldObj, newObj)

我不希望我的客户需要等待正在进行订单读取线程中的锁定。

而且我绝对不希望客户阻塞我的订单读取线程。

如果我使用Interlocked,会更好吗?

感谢您的建议!

2个回答

4

是的,最好使用Interlocked,因为它更高效,大部分情况下只需一个原子操作即可完成翻译。

然而,如果您不介意客户端在一段时间内仍然读取旧对象,甚至可以不使用Interlocked,而只需设置一个新实例。

那些恰好得到新实例的客户端将获得更新后的数据,而那些没有获得新实例的客户端将在下一次检查中获得更新后的数据。


那么我只需要取消客户端请求周围的锁定,并使用Interlocked更改跨线程变量吗? - Christophe

2

如果您的单个生产者正在创建一个全新的模型,并将其分配给共享字段,那么是的,Interlocked.Exchange是这里的正确选择。

如果您的生产者不需要知道旧模型是什么,那么可以使用Volatile.Write(ref _sharedObj, newObj)来保持简单。

只需注意以下3点:

  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);
    

嗨,在这种情况下,使用Interlocked更容易,因为它只需要我做1个操作而不是volatile,其中我需要进行读/写操作。另一个问题:当使用Interlocked时,我会丢失客户请求上的锁吗?如果它们具有1次旧数据迭代,我不在乎。谢谢! - Christophe
@user2565518 是的 - 如果模型是不可变的(对象本身而不是对它的引用),那么客户端可以简单地复制引用(例如 var obj = _sharedObj)并使用它。 - dcastro
为什么需要创建一个引用副本? - Christophe
第二点讨论了使用Volatile.read,而不是关于Interlocked场景来切换值和用户不使用synclock的情况。 - Christophe
@user2565518的第二点说明了读取共享引用两次可能会导致不同的值 - 这就是为什么您需要提前复制引用,然后使用该副本的原因。 - dcastro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接