在C语言中无锁线程之间共享布尔值

在C语言中无锁线程之间共享布尔值,c,linux,multithreading,pthreads,C,Linux,Multithreading,Pthreads,我有两个线程,分别称为服务器线程和计算线程。服务器线程启动计算线程,向其传递一个指向布尔变量的指针,即“停止标志”。当服务器线程将stop标志设置为true时,计算线程应该退出其计算循环并从run函数返回 我的问题是,有没有办法在两个线程之间安全地共享该标志,而不使用任何类型的锁(互斥锁、信号量等)?请记住: 标志将设置为true一次 一旦设置为true,标志将永远不会设置回false 只有一个服务器线程和一个计算线程 在这种情况下,我将“安全”定义为任何一种方法,在这种方法中,将服务器线程

我有两个线程,分别称为服务器线程和计算线程。服务器线程启动计算线程,向其传递一个指向布尔变量的指针,即“停止标志”。当服务器线程将stop标志设置为true时,计算线程应该退出其计算循环并从run函数返回

我的问题是,有没有办法在两个线程之间安全地共享该标志,而不使用任何类型的锁(互斥锁、信号量等)?请记住:

  • 标志将设置为true一次
  • 一旦设置为true,标志将永远不会设置回false
  • 只有一个服务器线程和一个计算线程
在这种情况下,我将“安全”定义为任何一种方法,在这种方法中,将服务器线程中的标志设置为true可以保证在合理的时间内反映计算线程中的真实值。(如果你的答案中包含“最终”一词,它可能无法满足“合理的时间”)


我对便携式和非便携式解决方案都感兴趣。我在Linux平台上运行,但除此之外还有一些灵活性:GCC、Clang、内核版本、C标准版本等等。

在这里将标志(您的
bool
)声明为
volatile
就足够了。这只会禁用此变量的缓存,并且保证每次读取都返回当前值。

在这里将标志(您的
bool
)声明为
volatile
就足够了。这只会禁用此变量的缓存,并且保证每次读取都返回当前值。

在这种情况下,将变量定义为
volatile
就足够了

单次写入不可能导致损坏

无法保证写入内容的可见时间,但您不需要


volatile
可防止有害的优化-如果编译器发现某个变量未被修改,它可能会认为不需要从内存中重新读取它
volatile
可以防止这种情况。

在这种情况下,将变量定义为
volatile
就足够了

单次写入不可能导致损坏

无法保证写入内容的可见时间,但您不需要


volatile
可防止有害的优化-如果编译器发现某个变量未被修改,它可能会认为不需要从内存中重新读取它<代码>易失性可防止这种情况。

用于此目的的正确数据类型是最新的C标准C11中的新类型
原子标记
和附带函数。大多数编译器还没有,但是有类似的东西可以用来模拟。例如,gcc和相关公司有
\uuuu sync\u lock\u test\u和
\uuuu sync\u lock\u release
用于相同目的

一般来说,仅仅像其他人建议的那样声明一个变量
volatile
,是不够的。尽管对于
int
\u Bool
来说很少见,但如果这样一个变量位于缓存边界之外,则对它的读写可能是部分的。例如,这可能导致设置变量的高阶位(导致解释为
true


如果此语义不足以满足您的需要,您可以使用其他原子操作,例如
\uu sync\u bool\u compare\u和\u swap
用于此目的的正确数据类型是新的类型
原子\u标志
以及最近的C标准C11中附带的函数。大多数编译器还没有,但是有类似的东西可以用来模拟。例如,gcc和相关公司有
\uuuu sync\u lock\u test\u和
\uuuu sync\u lock\u release
用于相同目的

一般来说,仅仅像其他人建议的那样声明一个变量
volatile
,是不够的。尽管对于
int
\u Bool
来说很少见,但如果这样一个变量位于缓存边界之外,则对它的读写可能是部分的。例如,这可能导致设置变量的高阶位(导致解释为
true


如果此语义不足以满足您的需要,您可以使用其他原子操作,例如
\uuu sync\u bool\u compare\u和\u swap

合理的时间量取决于计算循环的内容。合理的时间量取决于计算循环的内容。否,您还必须向编译器说明,它必须在内存中备份变量。@JensGustedt,
volatile
执行此操作。我看不出
volatile
在哪里执行此操作<代码>寄存器
易失性
绝不矛盾,编译器完全有权不在内存中备份易失性变量。
volatile
唯一禁止的是关于变量值的某些假设。@JensGustedt,
volatile
禁止关于如何修改变量的假设(参见C99 6.7.3-6)。如果不使用内存对其进行备份导致另一个线程的写入无效,那么它必须使用内存对其进行备份。如果它能够以某种方式将它保存在寄存器中,同时确保来自另一个线程的写入生效,那么就没有问题。不,您还必须向编译器明确说明它必须在内存中备份变量。@JensGustedt,
volatile
做到了这一点。我不知道
volatile
在哪里做到这一点<代码>寄存器
易失性
绝不矛盾,编译器完全有权不在内存中备份易失性变量。
volatile
唯一禁止的是关于变量值的某些假设。@JensGustedt,
volatile
禁止关于如何修改变量的假设(参见C99 6.7.3-6)。如果不使用内存对其进行备份,则会导致另一次写入
/* in file scope */
int work_has_been_initiated = 0;


/* inside the threads */
if (!__sync_lock_test_and_set(&work_has_been_initiated, 1)) {
   /* do the work, here */
}