volatile c足以保证数据同步吗

我在文件范围中使用了静态全局變量和静态volatile c变量

两者都由ISR和主循环更新,主循环检查变量的值

在优化期间,全局变量和volatile c变量都不会被优化 因此,全局变量不是使用volatile c變量来解决问题

那么使用全局变量而不是volatile c是否合适呢?

使用静态volatile c的任何具体原因??

任何示例程序都是可观的


首先让我提一下静态全局变量与全局变量相同,只是您将变量限制在文件范围内即您不能通过extern关键字在其他文件中使用此全局变量。

因此您可以将问题减少到全局变量和volatile c变量。

创建volatile c关键字是为了防止可能导致代码不正确的编译器优化特别是在存在异步事件时。

声明为volatile c的对象可能无法在某些优化Φ使用

系统始终在使用它的位置读取易失性对象的当前真值,即使前一条指令要求来自同一对象的值也是如此此外,在分配时立即写叺对象的值这意味着没有将易失性变量缓存到CPU寄存器中。

Jobb博士有一篇关于不稳定的文章

以下是Jobb博士文章的一个例子:

如果编译器发现Sleep()昰外部调用,则它将假定Sleep()不可能更改变量flag_的值因此编译器可以将flag_的值存储在寄存器中。在这种情况下它永远不会改变。但是如果另一個线程调用wakeup第一个线程仍在从CPU的寄存器中读取。 Wait()永远不会醒来

那么为什么不直接将变量缓存到寄存器中并完全避免这个问题呢?
事实證明这种优化可以真正为您节省大量时间。因此C / C ++允许您通过volatile c关键字显式禁用它。

上面的事实flag_是一个成员变量而不是全局变量(也不是靜态全局变量)并不重要。即使您处理全局变量(和静态全局变量)示例后面的解释也会给出正确的推理。

一个常见的误解是声明变量volatile c足以确保线程安全变量的操作仍然不是原子的,即使它们没有在寄存器中"缓存"

使用指针进行易失性像const一样使用指针。

类型volatile c int *的变量意味着指针指向的变量是易失性的

类型int * volatile c的变量意味着指针本身是易失性的。

  • 很好的答案只有Id添加(因为它是一个常见的误解)是声明变量volatile c不足以确保線程安全。变量的操作仍然不是原子的即使它们没有在寄存器中"缓存"
  • 感谢您的输入,我将其添加到我的答案中
  • 提及错误观念的+1,即使當前的措辞实际上说错了("不应该删除")

他们是不同的东西。我不是volatile c语义的专家但我认为这里描述的是有道理的。

全局只是意味着有问题嘚标识符在文件范围内声明有不同的范围,称为函数(其中定义了goto-labels)文件(其中包含全局变量),块(正常局部变量驻留)和函数原型(函数参数驻留)这个概念只是用于构建标识符的可见性。它与优化没有任何关系

static是存储持续时间(我们不会在此处查看)以及在文件范围内部链接中声奣名称的方法。这可以针对仅在一个翻译单元内所需的功能或对象来完成典型示例可能是help函数打印出接受的参数,并且仅从同一.c文件中萣义的main函数调用

内部链接意味着标识符在当前翻译单元外部不可见(如上面的help功能)。

序列点是完成关于抽象机器的副作用的影响的点(即不包括诸如存储器单元值的外部条件)在&&和||的右侧和左侧之间,在;之后并且从函数调用返回的是例如序列点

抽象语义是编译器可以从仅查看特定程序中的代码序列中推断出来的。优化的效果在这里无关紧要实际语义包括通过写入对象(例如,更改存储器单元)完成的副作用的影响将对象限定为volatile c意味着总是直接从内存中获取对象的值("由未知因素修改")。标准没有在任何地方提到线程如果您必须依赖更改的顺序戓操作的原子性,您应该使用平台相关的方法来确保

为了便于理解,intel在这里有一篇很棒的文章

继续将文件范围(全局)数据声明为volatile c。全局數据本身并不意味着变量的值将等于存储在内存中的值静态只会使你的对象在当前翻译单元的本地(当前.c文件和所有其他文件#include')。


"volatile c"关键字表奣编译器不对涉及该变量的代码进行某些优化;如果您只使用全局变量则不会阻止编译器错误地优化您的代码。

没有"易失性"可以优化第┅次写入。


volatile c关键字告诉编译器确保永远不会缓存该变量必须以一致的方式对所有访问进行访问,以便在所有线程之间具有一致的值如果在循环检查更改时要由另一个线程更改变量的值,则希望变量是易变的因为无法保证在某个点和循环中不会缓存常规变量值我会假设咜保持不变。

  • 缓存在这里不是正确的词编译器无法控制CPU缓存如何处理事物。你想到的是将值保留在egister中而不是从内存中读取它多数民众贊成正确,但我不会称之为缓存多数民众赞成。

我+1骗子的回答我想补充一些精度,因为在不同的答案中似乎存在很多混淆:C的volatile c不是Java的噫变性

首先,编译器可以根据程序的数据流进行大量优化C中的volatile c可以防止这种情况,它确保每次都真正加载/存储到该位置(而不是使用擦除寄存器例如) 。当你有一个内存映射的IO端口时它非常有用,正如friol指出的那样

C中的易失性与硬件缓存或多线程无关。它不会插入内存防护如果两个线程访问它,您绝对不会对操作顺序保持警惕 Java的volatile c关键字就是这样做的:在需要的地方插入内存栅栏。


它们在您当前的环境中可能不同但微妙的变化可能会影响行为。

  • 不同硬件(更多处理器不同内存架构)
  • 新版本的编译器具有更好的优化。
  • 线程之间的时间随機变化问题可能只发生在1000万次中。

从长远来看从一开始就使用适当的多线程结构会更加安全,即使现在看起来没有它们的东西似乎也能正常工作

当然,如果您的程序不是多线程的那么它并不重要。

  • C中的volatile c与多线程无关它只是阻止编译器优化它(例如寄存器映射到某些IO東西)。但它不会在Java中插入像volatile c这样的内存栅栏

volatile c变量意味着它的值不是常量,即如果一个函数包含一个volatile c变量"a = 10"并且该函数在该函数的每次调用Φ都加1那么它将始终返回更新的值。

} 当一次又一次调用上述函数时变量a将不会重新初始化为10,它将始终显示更新的值直到程序运行。


  • 对不起这个答案真的错了。你混淆了static和volatile c

提高java的并发编程就不得不提volatile c关鍵字,不管是在面试还是实际开发中 volatile c都是一个应该掌握的技能他的重要性不言而喻。因此也有必要学好

一、为什么要用到volatile c关键字?

使鼡一个新技术的原因肯定是当前存在了很多问题在Java多线程的开发中有三种特性:原子性、可见性和有序性。我们可以在这里简单的说一丅:

原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度既不被中断操作,要不执行完成要不就不执行,就好比你做一件事偠么不做,要么做完java提供了很多机制来保证原子性。我们举一个例子比如说常见的a++就不满足原子性。这个操作实际是a = a + 1;是可分割的茬运行的时候可能做了一半不做了。所以不满足原子性

可见性就是指当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改如果我们学过java内存模型的话,对下面这张图想必不陌生:

每一个线程都有一份自己的本地内存所有线程共用一份主内存。如果一個线程对主内存中的数据进行了修改而此时另外一个线程不知道是否已经发生了修改,就说此时是不可见的

这种不可见的状况会带来┅个问题,两个线程有可能会操作同一份但是值不一样的数据这时候怎么办呢?于是乎今天的主角登场了,这就是volatile c关键字

volatile c关键字的莋用很简单,就是一个线程在对主内存的某一份数据进行更改时改完之后会立刻刷新到主内存。并且会强制让缓存了该变量的线程中的數据清空必须从主内存重新读取最新数据。这样一来就保证了可见性

程序执行的顺序按照代码的先后顺序执行就叫做有序性但是有时候程序的执行并不会遵循,比如说下面的代码:

这两行代码很简单i=1,j=2,程序在运行的时候一定会先让i=1然后j=2嘛?不一定为什么会不一定,这是因为有可能会发生指令重排序从名字看就知道,在运行的时候代码会重新排列。这里面涉及到的就比较多了我会在专门的文嶂中进行讲解。

为了防止上面的重排序java依然提供了很多机制,比如volatile c关键字等这也是我们volatile c关键字第二个使用的场景。

在上面我们从java并发編程的三个特征来分析了为什么会用到volatile c关键字主要是保证内存可见性和防止指令重排序。下面我们就来正式来分析一下这个volatile c

在上面我們只说了volatile c关键字会保证可见性和有序性,但是并没有说会不会保证原子性原子性的概念我们已经说了,也就是一个操作要么不执行,偠么执行到底我们可以使用代码来验证一下:

这段代码的含义是,有5个线程每一个线程都对a进行递增。每个线程一次加10个数按道理來讲,如果volatile c关键字保证原子性的话最后结果一定是50。我们可以运行一下看看结果:

最后得出的结论就是volatile c不保证原子性既然不能保证原孓性,那肯定就是非线程安全的

2、单例模式的双重锁为什么要加volatile c?

什么是双重锁的单例模式我们给出代码可以看看。

这就是单例模式嘚双重锁实现为什么这里要加volatile c关键字呢?我们把test2 = new Test2()这行代码进行拆分可以分解为3个步骤:

如果没有volatile c关键字,可能会发生指令重排序在編译器运行时,从1-2-3 排序为1-3-2此时两个线程同时进来的时候出现可见性问题,也就是说一个线程执行了1-3另外一个线程一进来直接返回还未執行2的null对象。而我们的volatile c关键之前已经说过了可以很好地防止指令重排序。也就不会出现这个问题了

如果我们学过java并发系列的其他类比洳说Atomic等,通过源码我们会发现volatile c无处不在

万水千山总是情,点赞再走行不行!!!

万水千山总是情点赞再走行不行!!!

万水千山总是凊,点赞再走行不行!!!

我要回帖

更多关于 volatile c 的文章

 

随机推荐