c volatile一文搞懂

发布时间 2023-08-04 13:16:22作者: 浪刀刀

关键字

volatile

简单来说就两个作用:

  1. 保证不被编译器优化

    例如重复的赋值

    volatile int value = 10;
    value = 10;
    value = 10;
    

    volatile修饰后,一是这几个语句都会得到执行,二是顺序不会改变

  2. 保证可见性

    这里可见性的意思是,在多线程的环境下,当一个线程对一个变量改变,另一个线程能够知道当前的值已经改变,即:可见性的变量,在cpu改变他的值后,会立即写入内存中,而不是停留在cpu的cach中(基本cpu读写变量不是直接读写,而是缓存在cach中,有时cpu运算数据会直接读写到cach上,不能够及时写入内存)。

    可见,虽然volatile能够保证可见性,但是不能保证操作的原子性,原因如下:

    原子操作要求不能打断整个操作流程,但volatile修饰的变量可以在其他线程被改变。要保证原子性需要添加相关机制如:自旋锁。

链接:

  1. 内核中为什么不应该使用volatile关键字

    https://lp007819.wordpress.com/2013/01/20/%E5%86%85%E6%A0%B8%E4%B8%AD%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BA%94%E8%AF%A5%E4%BD%BF%E7%94%A8volatile%E5%85%B3%E9%94%AE%E5%AD%97/
    
  2. ​ chat_gpt问答:

    为什么定义了volatile还是不能保证原子操作
    ChatGPT
    定义了 volatile 只能保证对变量的读写操作是可见的,并且编译器不会对访问该变量的指令进行优化,但它不能保证原子操作。原子操作是指在多线程环境下,一个操作要么完全执行成功,要么完全不执行,没有中间状态,不会被其他线程打断。
    
    虽然 volatile 告诉编译器不要优化对该变量的访问,但在多线程环境中,仍然可能发生并发问题。例如,如果多个线程同时对同一个 volatile 变量进行自增操作,由于没有加锁或其他同步机制,可能会出现竞争条件,导致结果不是预期的。这时候,volatile 不能保证原子性,因为多个线程之间可能会同时读取、修改和写回变量,而不考虑其他线程的操作。
    
    要实现原子操作,需要使用其他同步机制,比如使用互斥锁(mutex)、原子操作库或并发容器。这些机制可以确保在多线程环境下,某个特定代码块或操作只能被一个线程执行,从而保证原子性和线程安全性。
    
    总结:volatile 只能保证可见性和禁止编译器优化,但不能保证原子操作。要实现原子操作和线程安全性,需要使用其他同步机制来确保在多线程环境中操作的原子性。
    
    
    
    
    User
    这里的读写可见性是是什么意思
    ChatGPT
    在多线程编程中,可见性是指一个线程对共享变量的修改能够被其他线程及时看到的特性。当一个线程修改了某个共享变量的值,其他线程在之后访问该变量时能够看到最新的值,而不是之前缓存的旧值。
    
    可见性问题主要涉及到多线程之间的内存可见性。现代计算机体系结构中,每个线程都有自己的缓存(CPU cache),这些缓存用于加快对内存的访问速度。当一个线程对共享变量进行修改时,它首先会将修改后的值存储在自己的缓存中,并且在合适的时机将这个修改刷新到主内存中。其他线程在访问这个共享变量时,可能会直接从自己的缓存中读取值,而不是从主内存中读取。这就可能导致多个线程之间看到的是不一致的值,即出现了内存可见性问题。
    
    为了解决内存可见性问题,可以使用同步机制,如互斥锁(mutex)或原子操作,来确保对共享变量的读写是原子的,从而保证修改的可见性。当一个线程获取了锁或执行了原子操作时,其他线程必须等待,直到这个操作完成,从而保证了对共享变量的读写操作对其他线程是可见的。
    
    volatile 关键字也提供了可见性的保证。当一个变量被声明为 volatile 时,对该变量的写操作会立即刷新到主内存,而对该变量的读操作会从主内存中获取最新的值。这样可以保证多线程之间对该变量的修改对其他线程是可见的。
    
    总结:可见性是指多线程之间对共享变量修改的可见性。为了保证可见性,需要使用同步机制或 volatile 关键字来确保对共享变量的读写操作是原子的,并及时刷新到主内存,从而保证其他线程能够看到最新的值。
    
    
    
    
    User
    在这里,既然已经保证可见性了,那为什么还不能保证是原子操作
    ChatGPT
    您是正确的,我之前的回答有一点混淆。在多线程编程中,volatile 关键字主要用于保证可见性,但它并不能保证原子操作。
    
    当一个变量被声明为 volatile 后,对该变量的写操作会立即刷新到主内存,而对该变量的读操作会从主内存中获取最新的值,确保了多个线程之间对变量值的可见性。这意味着当一个线程修改了 volatile 变量的值,其他线程可以立即看到最新的值,而不是过期的缓存值。
    
    然而,volatile 关键字并不能保证原子操作。原子操作是指一个操作要么完全执行成功,要么完全不执行,没有中间状态。在多线程环境中,多个线程同时对同一个变量进行读写操作时,仍然可能出现竞争条件和数据不一致的问题。即使对一个 volatile 变量进行自增或自减操作,在高并发情况下,多个线程同时读取并修改该变量,可能导致结果不是预期的。
    
    要实现原子操作,需要使用其他同步机制,比如使用互斥锁(mutex)、原子操作库或并发容器。这些机制可以保证在多线程环境下,某个特定代码块或操作只能被一个线程执行,从而保证了原子性和线程安全性。
    
    因此,volatile 关键字在多线程编程中主要用于保证对变量的可见性,而不是用于实现原子操作。要实现原子操作,需要使用其他同步手段来确保操作的原子性。
    

    转载请附本文链接