当只有一个线程写入共享变量时,是否需要锁定?

Dan*_*son 9 c multithreading mutex locking

我有2个线程和一个共享float全局.一个线程只写入变量,而另一个只读取它,我是否需要锁定对此变量的访问?换一种说法:

volatile float x;

void reader_thread() {
    while (1) {
        // Grab mutex here?
        float local_x = x;
        // Release mutex?
        do_stuff_with_value(local_x);
    }
}

void writer_thread() {
    while (1) {
        float local_x = get_new_value_from_somewhere();
        // Grab mutex here?
        x = local_x;
        // Release mutex?
    }
}
Run Code Online (Sandbox Code Playgroud)

我主要关注的是,一个的加载或存储float不是原子,使得local_xreader_thread结束有一个假的,部分更新后的值.

  1. 这是一个有效的问题吗?
  2. 有没有另一种方法可以在没有互斥锁的情况下保证原子性?
  3. 使用sig_atomic_t作为共享变量的工作,假设它有足够的位用于我的目的?

有问题的语言是C使用pthreads.

eph*_*ent 13

不同的体系结构具有不同的规则,但通常,对齐的int大小对象的内存加载和存储是原子的.更小和更大可能是有问题的.所以,如果sizeof(float) == sizeof(int)你可能是安全的,但我仍然不会在便携式程序中依赖它.

此外,行为volatile没有特别明确定义......规范使用它作为一种方法来防止优化对内存映射设备I/O的访问,但没有说明它在任何其他内存访问上的行为.

简而言之,即使加载和存储是原子的float x,我也会使用显式内存屏障(尽管平台和编译器有多大不同),而不是依赖于volatile.如果没有加载和存储是原子的保证,你将不得不使用锁,这意味着内存障碍.


Ada*_*eld 5

根据GNU C库文档的第24.4.7.2节:

在实践中,您可以假设int和其他整数类型不再int是原子的.您还可以假设指针类型是原子的; 这很方便.这两个假设都适用于GNU C库支持的所有机器以及我们所知道的所有POSIX系统.

float从技术上讲,这些规则并不算数,尽管如果a floatint您的体系结构的大小相同,那么您可以做的是使您的全局变量成为一个int,然后在每次读取或写入时将其转换为带有并集的浮点数.

最安全的做法是使用某种形式的互斥锁来保护对共享变量的访问.由于关键部分非常小(读/写一个变量),你几乎可以肯定会从轻量级互斥锁(如自旋锁)中获得更好的性能,而不是制造系统的重量互斥体要求做好自己的工作.