简体   繁体   English

此代码是线程安全的吗?

[英]Is this code thread-safe?

Let's say we have a thread-safe compare-and-swap function like 假设我们有一个线程安全的比较和交换功能,例如
long CAS(long * Dest ,long Val ,long Cmp)
which compares Dest and Cmp , copies Val to Dest if comparison is succesful and returns the original value of Dest atomically. 比较DestCmp ,如果比较成功,则将Val复制到Dest ,并自动返回Dest的原始值。

So I would like to ask you if the code below is thread-safe. 所以我想问你下面的代码是否是线程安全的。

while(true)
{
    long dummy = *DestVar;
    if(dummy == CAS(DestVar,Value,dummy) )
     {
        break;
    }

}

EDIT: 编辑:
Dest and Val parameters are the pointers to variables that created on the heap. Dest和Val参数是指向在堆上创建的变量的指针。 InterlockedCompareExchange is an example to out CAS function. InterlockedCompareExchange是输出CAS函数的示例。

Edit. 编辑。 An edit to the question means most of this isn't relevant. 对问题进行编辑意味着大多数与此无关。 Still, I'll leave this as all the concerns in the C# case also carry to the C++ case, but the C++ case brings many more concerns as stated, so it's not entirely irrelevant. 尽管如此,我将保留这一点,因为C#案例中的所有关注点也都包含在C ++案例中,但是C ++案例带来了更多的关注,因此并不是完全无关紧要的。


Yes, but... 对,但是...

Assuming you mean that this CAS is atomic (which is the case with C# Interlocked.CompareExchange and with some things available to use in some C++ libraries) the it's thread-safe in and of itself. 假设您的意思是说这个CAS是原子的(C# Interlocked.CompareExchange就是这种情况,并且在某些C ++库中可以使用某些东西),它本身就是线程安全的。

However DestVar = Value could be thread-safe in and of itself too (it will be in C#, whether it is in C++ or not is implementation dependent). 但是DestVar = Value本身也可以是线程安全的(无论在C ++中还是在实现中,它都将在C#中使用)。

In C# a write to an integer is guaranteed to be atomic. 在C#中,对整数的写保证是原子的。 As such, doing DestVar = Value will not fail due to something happening in another thread. 这样,执行DestVar = Value不会因另一个线程中发生的事情而失败。 It's "thread-safe". 这是“线程安全的”。

In C++ there are no such guarantees, but there are on some processors (in fact, let's just drop C++ for now, there's enough complexity when it comes to the stronger guarantees of C#, and C++ has all of those complexities and more when it comes to these sort of issues). 在C ++中,没有这样的保证,但是在某些处理器上有保证(实际上,让我们暂时放下C ++,就更强大的C#保证而言,有足够的复杂性,而C ++具有所有这些复杂性,而涉及到的复杂性更多)这些问题)。

Now, the use of atomic CAS operations in themselves will always be "thead-safe", but this is not where the complexity of thread safety comes in. It's the thread-safety of combinations of operations that is important. 现在,原子CAS操作本身的使用始终将是“安全的”,但这并不是线程安全的复杂性所在。重要的是,操作组合的线程安全。

In your code, at each loop either the value will be atomically over-written, or it won't. 在您的代码中,在每个循环中,该值要么被原子覆盖,要么不会被覆盖。 In the case where it won't it'll try again and keep going until it does. 如果无法使用,它将再次尝试并继续尝试直到成功为止。 It could end up spinning for a while, but it will eventually work. 它可能最终会旋转一段时间,但最终会起作用。

And in doing so it will have exactly the same effect as simple assignment - including the possibility of messing with what's happening in another thread and causing a serious thread-related bug. 这样做的效果与简单分配完全相同-包括弄乱另一个线程中发生的事情并导致严重的线程相关错误的可能性。

Take a look, for comparison, with the answer at Is this use of a static queue thread-safe? 为了进行比较,请看一下下面的答案, 这是否使用静态队列线程安全? and the explanation of how it works. 以及它是如何工作的。 Note that in each case a CAS is either allowed to fail because its failure means another thread has done something "useful" or when it's checked for success more is done than just stopping the loop. 请注意,在每种情况下,CAS都可能因失败而失败,因为它的失败意味着另一个线程做了“有用的事”,或者当检查成功时,不仅仅是停止循环而已。 It's combinations of CASs that each pay attention to the possible state caused by other operations that allow for lock-free wait-free code that is thread-safe. 它是CAS的组合,每个CAS都注意由其他操作导致的可能状态,这些其他操作允许使用线程安全的无锁免等待代码。

And now we've done with that, note also that you couldn't port that directly to C++ (it depends on garbage collection to make some possible ABA scenarios of little consequence, with C++ there are situations where there could be memory leaks). 现在,我们已经完成了此操作,还请注意,您无法将其直接移植到C ++(它依赖于垃圾回收,以使某些可能的ABA场景产生的影响很小,而C ++则存在内存泄漏的情况)。 It really does also matter which language you are talking about. 您说的是哪种语言也确实很重要。

It's impossible to tell, for any environment. 在任何环境下都无法分辨。 You do not define the following: 您没有定义以下内容:

  • What are the memory locations of DestVar and Value ? DestVarValue的存储位置是什么? On the heap or on the stack? 在堆还是堆栈上? If they are on the stack, then it is thread safe, as there is not another thread that can access that memory location. 如果它们在堆栈上,那么它是线程安全的,因为没有其他线程可以访问该内存位置。

  • If DestVar and Value are on the heap, then are they reference types or value types (have copy by assignment semantics). 如果DestVarValue在堆上,那么它们是引用类型还是值类型(具有按分配语义复制)。 If the latter, then it is thread safe. 如果是后者,则是线程安全的。

  • Does CAS synchronize access to itself? CAS是否同步对其自身的访问? In other words, does it have some sort of mutual exclusion strucuture that has allows for only one call at a time? 换句话说,它是否具有某种互斥结构,一次只能允许一个呼叫? If so, then it is thread-safe. 如果是这样,则它是线程安全的。

  • If any of the conditions mentioned above are untrue, then it is indeterminable whether or not this is all thread safe. 如果上述任何条件都不成立,则不确定是否所有线程安全。 With more information about the conditions mentioned above (as well as whether or not this is C++ or C#, yes, it does matter ) an answer can be provided. 有了有关上述条件的更多信息(以及这是否为C ++或C#, 是的,这确实很重要 ),就可以提供答案。

Actually, this code is kind of broken. 实际上,此代码有点破损。 Either you need to know how the compiler is reading *DestVar (before or after CAS ), which has wildly different semantics, or you are trying to spin on *DestVar until some other thread changes it. 您可能需要知道编译器如何读取*DestVar (在CAS之前或之后),它具有截然不同的语义,或者您试图在*DestVar上旋转直到其他线程对其进行了更改。 It's certainly not the former, since that would be crazy. 当然不是前者,因为那太疯狂了。 If it's the latter, then you should use your original code. 如果是后者,则应使用原始代码。 As it stands, your revision is not thread safe, since it isn't safe at all. 就目前而言,您的修订版不是线程安全的,因为它根本不安全。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM