假设我们有一个线程安全的比较和交换功能,如
long CAS(long * Dest ,long Val ,long Cmp)
比较Dest
和Cmp
,如果比较成功则将Val
复制到Dest
并以原子方式返回Dest的原始值。
所以我想问你下面的代码是否是线程安全的。
while(true)
{
long dummy = *DestVar;
if(dummy == CAS(DestVar,Value,dummy) )
{
break;
}
}
编辑:
Dest和Val参数是指向堆上创建的变量的指针。
InterlockedCompareExchange是CAS功能的一个例子。
答案 0 :(得分:2)
编辑。对问题的编辑意味着大部分内容都不相关。尽管如此,我还是会离开这一点,因为C#案例中的所有问题也都带有C ++案例,但C ++案例带来了更多关注的问题,所以它并非完全不相关。
是的,但是......
假设你的意思是这个CAS是原子的(C#Interlocked.CompareExchange
就是这种情况,并且在某些C ++库中有一些可用的东西),它本身就是线程安全的。
然而,DestVar = Value
本身也可以是线程安全的(它将在C#中,无论是否在C ++中都依赖于实现)。
在C#中,对整数的写入保证是原子的。因此,执行DestVar = Value
不会因另一个线程中发生的事情而失败。它是“线程安全的”。
在C ++中没有这样的保证,但是在某些处理器上(实际上,让我们暂时放弃C ++,当涉及C#的更强保证时,有足够的复杂性,而C ++具有所有这些复杂性和更多当涉及到这些问题时)。
现在,原子CAS操作本身的使用总是“安全的”,但这并不是线程安全的复杂性所在。这是操作组合的线程安全性很重要。
在你的代码中,在每个循环中,值将被原子覆盖,或者不会。如果它不会再次尝试并继续前进直到它。它可能最终会旋转一段时间,但它最终会起作用。
这样做会产生与简单分配完全相同的效果 - 包括弄乱另一个线程中发生的事情并导致严重的线程相关错误的可能性。
为了比较,请查看Is this use of a static queue thread-safe?的答案及其工作原理的说明。请注意,在每种情况下,CAS都被允许失败,因为它的失败意味着另一个线程已经做了“有用”的事情,或者当它被检查成功时,完成的工作多于停止循环。它是CAS的组合,每个CAS都注意由其他操作引起的可能状态,这些操作允许线程安全的无锁等待代码。
现在我们已经完成了这一点,还要注意你不能直接将它移植到C ++(它依赖于垃圾收集来制作一些可能的ABA场景,而C ++存在可能存在内存的情况泄漏)。你说的是哪种语言真的很重要。
答案 1 :(得分:1)
对于任何环境,都无法分辨。您没有定义以下内容:
DestVar
和Value
的内存位置是多少?在堆上还是堆栈上?如果它们在堆栈上,则它是线程安全的,因为没有其他线程可以访问该内存位置。
如果DestVar
和Value
在堆上,那么它们是引用类型还是值类型(通过赋值语义进行复制)。如果是后者,则它是线程安全的。
CAS
是否同步访问自身?换句话说,它是否具有某种互斥排除结构,一次只允许一个呼叫?如果是这样,那么它是线程安全的。
如果上述条件的任何都是不真实的,那么 indeterminable 是否全部是线程安全的。有关上述条件的更多信息(以及这是否是C ++或C#,是,它确实重要)可以提供答案。
答案 2 :(得分:0)
实际上,这段代码有点破碎。您需要知道编译器如何读取*DestVar
(在CAS
之前或之后),它具有完全不同的语义,或者您试图在*DestVar
上旋转直到某个其他线程更改它。这肯定不是前者,因为那会很疯狂。如果是后者,那么您应该使用原始代码。就目前而言,您的修订版本不是线程安全的,因为它根本不安全。