C++11 学习笔记(10) std::atomic i; ++i 前缀自增运算的原子性分析

目前项目中使用到了多线程, 我选择 C++11 std::thread 库实现,
模仿开源的流媒体服务器 crtmpserver 里的 protocol类,
每生成一个实例类, 都会有全局的唯一id.
在整个函数声明周期里, 定义:
static u_int32_t idGlobalGenerator; // 初始化0
即当一个类生成时, 该实例类的私有成员 u_int32_t id_;
在构造函数里初始化 id_ = idGlobalGenerator++;
由于我的开发场景是多线程, 因此 idGlobalGenerator
被声明为:

执行 ++idGlobalGenerator; 操作, 根据文档和语义它是原子性的. 而在这里产生的疑惑是:

u_int32_t id = ++idGlobalGenerator;

该执行是否是原子性, 保证多线程环境下, 赋值的 id都是唯一的值.
于是我在 stackoverflow上直接提了问题(当时没有亲自验证): 链接

  

当天下午就有热心的开发者回答了, 并且给了详细的回复.
在此用简单的语言和图片总结下:

1. 在 visual studio 2013 下调试;

调试进去后的调用情况:

图中标亮的区域为调用的函数块,
调用了 atomic_fetch_add, 该步属于原子性操作, 自加后返回先前的值.
++i 操作首先获得 i之前的值, 然后与 1相加返回

2. u_int32_t id = ++idGlobalGenerator; 不是原子性操作, 但 id 是值全局唯一的.
因为由第 1 点, atomic_fetch_add 之后返回的值是 idGlobalGenerator 自加之前的值, 这个值即全局唯一,只不过在这个值基础上 +1 操作, 该步骤已经不是原子性了,
但依然能保证计算出的值全局唯一. (因为前提条件的值是原子性的, 逻辑推导~~)
所以 id 的值能保证全局唯一.

3. 验证本身是可以自己做的事情, 而当时没有思考验证方法就直接提问, 解决问题的方式需要改进.
4. stackoverflow 上提问时, 代码放了一大块, 原先描述问题的内容很少, 导致发出提问失败,
后来搜索失败原因, 是描述问题不够详细, stackoverflow可能做了描述信息字数的标准, 认为太少问题描述对问题本身
信息量不够. 为保证问题的可读性, 要求提问者详细描述.

参考资料:

  1. [C++11 atomic 与 mutex 性能比较]
  2. [参考资料1中提到的 github上的测试代码]

原创文章,转载请注明: 转载自kaka_ace's blog

本文链接地址: C++11 学习笔记(10) std::atomic i; ++i 前缀自增运算的原子性分析

发表评论

电子邮件地址不会被公开。 必填项已用*标注