如何确保比较结果在多线程中仍然成立?

Posted

技术标签:

【中文标题】如何确保比较结果在多线程中仍然成立?【英文标题】:How to ensure the comparison result still hold in multi-threading? 【发布时间】:2022-01-18 12:13:17 【问题描述】:

假设有3个线程,

线程 1 和 2 将自动增加或减少全局变量 X。

线程 1:

atomic_increase(X)

线程 2:

atomic_decrease(X)

线程 3 将检查 X 是否大于某个预定义的值并相应地执行操作。

线程 3:

if( X > 5 ) ... logic 1 ...
else ... logic 2 ....

我认为atomic_xxx 操作还不够。它们只能在线程 1 和 2 之间同步修改。

如果X被线程1或2更改在线程3完成比较后进入logic 1

修改或读取X时是否必须使用互斥锁来同步所有3个线程?

添加 1

顺便说一句,逻辑 1 和逻辑 2 不会修改 X。

【问题讨论】:

【参考方案1】:

简而言之,读取也需要以某种方式同步,否则读取不一致的风险是真实存在的。在atomic_increase 的读取和写入之间执行的读取将不一致。

但是,如果logic 1logic 2X 做事,您的问题似乎并没有就此停止。我认为您需要事务的概念,它以读取(X > 5 事物)开始,然后以写入(logic 1logic 2)结束。

【讨论】:

谢谢。顺便说一句,逻辑 1 和逻辑 2 不会修改 X。【参考方案2】:

是的,答案是happens before 链接,假设Thread-1 开始执行atomic_increase 方法。它将持有lock 并进入synchronized 块以更新X

private void atomic_increase() 
    synchronized (lock) 
        X = X + 1;  // <-- Thread-1 entered synchronized block, yet to update variable X
    

现在,Thread-3 要运行逻辑,它需要读取变量 X,如果是 not synchronized(在同一监视器上),则变量 X 读取可以是旧值,因为Thread-1 可能还没有更新。

private void runLogic() 
    if (X > 5)  // <-- Reading X here, can be inconsistent no 
                   happens-before between atomic_increase and runLogic
     else 

    

我们可以通过在atomic 操作和run_logic 方法之间维护happens-before 链接来防止这种情况发生。如果runLogicsynchronized(在同一监视器上),那么它必须等到变量XThread-1 更新。所以我们保证得到X的最后更新值

private void runLogic() 
    synchronized (lock) 
        if (X > 5)  // <-- Reading X here, will be consistent, since there 
                       is happens-before between atomic_increase and runLogic 
         else 

        
    

【讨论】:

【参考方案3】:

答案取决于您的应用程序做什么。如果逻辑 1 和逻辑 2 都没有修改 X,则很有可能不需要额外的同步(除了使用 atomic_load 读取 X)。

我假设您将内在函数用于原子操作,而不仅仅是互斥体(或 Java 中的同步块)中的增量。例如。在 Java 中,有一个 AtomicInteger 类,其中包含“incrementAndGet”和“get”等方法。如果你使用它们,可能不需要额外的同步,但这取决于你真正想用逻辑 1 或逻辑 2 实现什么。

如果你想,例如当X &gt; 5时显示一条消息,然后你就可以做到了。到消息显示时,X 的值可能已经改变,但事实仍然是,消息是由 X 至少在一段时间内大于 5 触发的。

换句话说,如果没有额外的同步,您只能保证如果X 大于 5 将调用逻辑 1,但不能保证在执行逻辑 1 期间它会保持这种状态。可能是对你好不好。

【讨论】:

所以要看逻辑1是什么。 @smwikipedia 这也取决于逻辑 2 是什么。当X &lt;= 5 时没有额外的同步逻辑2 将被调用,但在逻辑2 执行期间X 可能会并行增加,例如,6 或更多。如果逻辑 2 没问题,那就没问题了。

以上是关于如何确保比较结果在多线程中仍然成立?的主要内容,如果未能解决你的问题,请参考以下文章

Spring在多线程环境下如何确保事务一致性

如何在多线程程序中传递或共享开放流引用?

如何保证单例模式在多线程中的线程安全性

解释 gperftools 在多线程工作负载上的结果

程序在多线程中没有顺利结束

在多线程 C 应用程序中嵌入 python