mad*_*phy 4 c multithreading atomic thread-safety
我有一个名为my_list包含链接列表的全局变量(即,该变量是指向列表的第一个成员的指针)。该变量只能由两个线程(线程 A 和线程 B)编辑。如果列表变空,则该变量将设置为NULL。发生这种情况时,首先将变量设置为NULL,然后释放内存。
由于它是由两个线程编辑的,因此每次线程 A 或线程 B 接触该my_list变量时我都会使用互斥锁。到目前为止没有什么异常。
但是接下来出现了第三个线程,线程 C。该线程永远不会以任何方式接触链表,但它需要不时知道链表是否为空。所以,线程C唯一要做的就是
if (my_list) {
do_something_completely_unrelated();
}
Run Code Online (Sandbox Code Playgroud)
我必须为此使用互斥体吗?我相信这是一个原子操作,因此不需要互斥体。它是否正确?
编辑
我将在这里添加一些背景信息。我想避免使用互斥体的原因是列表很少(总是)更新,但来自线程 C 的检查每隔几毫秒发生一次,因此操作越少越好。
如果列表显示为非NULL,则线程 C 会触发使用互斥锁的正确检查,并且如果确认列表不为空,则线程 C 停止其强制检查。
根据该标准,如果至少有一个操作是非原子的,则在写入变量时读取该变量是未定义的行为。
从评论部分来看,您似乎想知道它是否会使您的程序崩溃,或者可能发生的最糟糕的事情是否是您得到错误的值。你的评论,我的重点:
谢谢。但具体会发生什么不好的事情呢?假设该变量正在
NULL被线程 A 更改为地址xxxxxx。线程 C 尝试读取它。能得到什么价值?要么NULL要么xxxxxx,两者都可以。我假设程序不会崩溃是错误的吗?
我想说这个简单的检查很可能不会导致你的程序崩溃。检查操作是安全的,因为您很可能会获得一个值。该值可能是错误的,但仅进行检查很可能不会使您的程序崩溃。
然而,这是未定义的行为:
如果程序的执行在不同线程中包含两个冲突的操作,并且至少其中一个不是原子的,并且两者都发生在另一个之前,则该程序的执行就包含数据竞争。任何此类数据竞争都会导致未定义的行为。
然后您发布了以下后续评论:
这实际上并不那么重要,但是我是否正确地假设,如果线程 A 将值从 更改为
NULL(xxxxxx或反之亦然),我肯定不会得到yyyyyy,但要么 NULL 要么xxxxxx?
在这里我想说你的假设是错误的。AFIK,标准中没有任何内容表明指针赋值必须是原子操作,如果是的话我会感到惊讶。正如我上面提到的,这是未定义的行为。
_Atomic关键字使用限定符声明列表_Atomic。它有一些限制。例如:
它不是强制性功能,因此可能会降低可移植性
它不能与数组一起使用
如果与结构体一起使用,则无法单独访问该结构体的字段
2 和 3 对你来说应该不重要,因为列表只是一个指针。
在这里阅读_Atomic: https: //en.cppreference.com/w/c/language/atomic
如果_Atomic这不是一个选项,这里是伪代码的解决方案:
if ( now() - lastUpdated > ms ) // If it was more than ms milliseconds since last update
lock(myMutex)
myListCopy = myList
unlock(myMutex)
lastUpdated = now()
if(myListCopy)
do_something_completely_unrelated();
Run Code Online (Sandbox Code Playgroud)
lastUpdated和myListCopy最好是函数的本地变量,但重要的是线程 A 和 B 永远不会接触它们。