我有两个线程,称它们为服务器线程和计算线程。服务器线程启动计算线程,并向其传递指向布尔变量"停止标志"的指针。当服务器线程将stop标志设置为true时,计算线程应退出其计算循环并从run函数返回。
我的问题是,有没有办法在不使用任何类型的锁(互斥锁,信号灯等)的情况下,在两个线程之间安全地共享该标志?记住:
标志将一次设置为true
一旦设置为true,标志将永远不会重新设置为false
只有一个服务器线程,只有一个计算线程
在这种情况下,我将"安全"定义为可以确保在合理的时间内在服务器线程中将标志设置为true的任何方法,以在计算线程中反映出true值。 (如果您的答案包含"最终"一词,则可能不满足"合理的时间")。
我对便携式和非便携式解决方案都感兴趣。我在Linux平台上运行,但除此之外还具有一些灵活性:GCC,Clang,内核版本,C标准版本等。
合理的时间量取决于计算循环的内容。
在这种情况下,将变量定义为volatile就足够了。
只需一次写入,就不可能损坏。
无法保证写入内容可见的时间,但是您不需要这样做。
volatile防止有害的优化-如果编译器发现未修改变量,则可能会认为它不需要从内存中重新读取它。 volatile防止这种情况。
不,您还必须向编译器说明必须将变量备份到内存中。
@JensGustedt,volatile执行此操作。
我看不到volatile在哪里执行此操作。 register和volatile绝不矛盾,并且编译器有权不备份内存中的易失性变量。 volatile禁止的唯一事情是有关变量值的某些假设。
@JensGustedt,volatile禁止有关如何修改变量的假设(请参阅C99 6.7.3-6)。如果不使用内存备份它导致另一个线程的写操作不起作用,则它必须使用内存备份它。如果它能以某种方式将其保存在寄存器中,同时确保来自另一个线程的写入确实生效-则没有问题。
在这里将标志(您的bool)声明为volatile就足够了。这只会禁用此变量的缓存,并且保证每次读取都返回当前值。
为此目的,正确的数据类型是新的atomic_flag类型以及最新的C标准C11附带的函数。大多数编译器还没有,但是可以使用类似的东西来模拟。例如,gcc和亲戚出于相同的目的使用__sync_lock_test_and_set和__sync_lock_release。
通常,仅仅声明变量volatile就像其他人建议的那样是不够的。尽管对于int或_Bool很少见,但是如果跨变量跨高速缓存边界读取或写入该变量,则可能是部分读取或写入。例如,这可能导致该变量的高阶位似乎已设置(导致解释为true)。
/* 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 */
}
如果此语义不足以实现您的目的,则可以使用其他原子操作,例如__sync_bool_compare_and_swap。
您能否举一个使用__sync_lock_test_and_set和__sync_lock_release的示例?
这是一个过大的杀伤力-如果只有一个线程修改该标志,则不需要原子操作。尽管我承认有时矫over过正会更好,但是万一代码变得更加复杂而又没人愿意更新同步。
@ugoren,不,这不是矫kill过正。在大多数体系结构上,这将导致非常简单的汇编代码,但是有些体系结构需要特别注意这些事项。知道是什么情况并生成适当的代码是编译器作者的工作。