Effective C++: volatile.

volatile和const一样是C++的类型修饰符(type specifier),即可以用来修饰变量,也可以用来修饰指针,甚至是用户自定义类型,函数等.

该关键字只能保证: 被volatile修饰的变量等每次访问时候都从所在内存获取.

volatile的字面意思是: 易变的,不稳定的.

C/C++编译器优化原则: 保证一段程序的运行在优化前后无变化.

case 1:

   int a =0; //在realse版本下a和b的值都会被读取cpu的寄存器中,以方便更加快速的访问.
   int b =0;

   b = a+1; //这里读取a的值并不是从内存读取,而从cpu寄存器中读取.

case 1.5:

   volatile int a=1;
   int b = 0;

   a = function(); //这里的值还是被直接写到a的所在的内存(注意是内存)地址里.
   
   b = a+1; //但是这里不同了!这里不再从寄存器中读取a的值了而是直接从a的地址中读取该值.

case 1.6:


  //这里的本意是在两个不同时刻获取 number_1 的内容.(其中number_1中的内容可能实时改变).
  //如果这里未使用volatile修饰,第一次读取到的内容是从内存中获取的,但是第二次读取为了节省时间就会从寄存器的缓存中获取
  //这样一来拿到的数据就不对了.
  voliatile int number_1{0};

  int number_2 = number_1;
  int number_3 = number_1;


case 2:


  int a;
  int b;
  int c;

  a = 1;
  b = 2;
  c = 3;
 
  std::cout<< a << b << c <<std::endl;
  //上面编译器会直接进行优化认为a, b, c是没用的,从而直接进行产量替换类似下面:
  // std::cout << 1 << 2 << 3 << std::endl;


  return 0;


case 2.5:


  volatile int a; //注意这里的volatile.
  volatile int b; //同上
  volatile int c; //同上
  
  a = 1;  //把1写到a的地址里.并且把a此时的值读到CPU寄存器里.
  b = 2;  //把2写到b的地址里.并且把b此时的值读到CPU寄存器里.
  c = 3;  //把3写到c的地址里.并且把c此时的值读到cpu寄存器里.

  std::cout << a << b << c <<std::endl; //从a, b, c的内存(注意是内存)地址中分别读取他们的值.
  
  return 0;
  

case 3:

int a;
int b;

void function()
{
  a = b + 1;
  b = 0; 

}

//上面的代码中我们理想的情况是:首先执行a = b+1;
//但是经过编译器的优化 b = 0;首先被执行了.也因为b是全局变量因此是优先初始化的.

case 3.5:

int a;
volatile int b; //注意这里.

void function()
{
  a = b +1;
  b = 0; //注意这里b = 0,其实是和默认初始化一样的效果因此被提前了.

}

//上面的代码中我们把b声明成了volatile类型,但是仍然没有阻止编译器的优化.
//b = 0;被提到了 a = b + 1;之前执行.

case 4:

volatile int a;
volatile int b;

void function()
{
   a = b + 1;
   b = 0;

}

//OK,编译器没有进行任何优化,按照我们写的顺序执行了.

总结: 

特性一:编译器对该变量的访问不再优化(即每次都直接从内存中读取值,而不是为了速度从cpu寄存器中读取)提供对地址的稳定访问

特性二:顺序性,保证代码按照我们所写的顺序执行(如果混合使用volatile变量和普通变量仍然有可能造成适当乱序)

错误的使用:volatile

#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

volatile bool boolean = false; //这是非常错误的用法!!!!!!!!!!!!!!!.
//最好用 std::aomic<bool> boolean(false);

void  doSomething(const int& id, const char& c)
{
	while (!boolean) {
		std::this_thread::yield();//注意这里.
	}

	for (volatile int i = 0; i < 10; ++i) { 
		std::cout << c;
	}
}

int main()
{
	std::thread threads[3];

	for (unsigned int i = 0; i < 3; ++i) {
		threads[i] = std::thread(doSomething, i, 'a' + i);
	}

	boolean = true;
	for (std::thread& ref_thread : threads) {
		ref_thread.join();
	}

	std::cout << '\n';

	return 0;
}

上面的代码虽然运行起来没有问题,但是却潜在一个很大的问题,因为这些线程是并行运行的虽然主线程把bool值设为了true,但是万一再有其他的线程把值设为false那么就有可能产生data race.

参考:《The C++ Standard library》988页,所说在C++中volatile即不提供atomicity(非原子的,也不提供特别的order,也不提供互斥性).

转载于:https://my.oschina.net/SHIHUAMarryMe/blog/2396121

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值