如何确保比较结果在多线程中仍然成立?
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 1
或logic 2
对X
做事,您的问题似乎并没有就此停止。我认为您需要事务的概念,它以读取(X > 5
事物)开始,然后以写入(logic 1
或 logic 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
链接来防止这种情况发生。如果runLogic
是synchronized
(在同一监视器上),那么它必须等到变量X
被Thread-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 > 5
时显示一条消息,然后你就可以做到了。到消息显示时,X
的值可能已经改变,但事实仍然是,消息是由 X
至少在一段时间内大于 5 触发的。
换句话说,如果没有额外的同步,您只能保证如果X
大于 5 将调用逻辑 1,但不能保证在执行逻辑 1 期间它会保持这种状态。可能是对你好不好。
【讨论】:
所以要看逻辑1是什么。 @smwikipedia 这也取决于逻辑 2 是什么。当X <= 5
时没有额外的同步逻辑2 将被调用,但在逻辑2 执行期间X
可能会并行增加,例如,6 或更多。如果逻辑 2 没问题,那就没问题了。以上是关于如何确保比较结果在多线程中仍然成立?的主要内容,如果未能解决你的问题,请参考以下文章