微信号:hxzqlh

介绍:我在这里,只写三件事:读书笔记,技术总结,谈笑风生.

C++11从入门到放弃|atomic_flag

2016-07-18 19:45 黄学忠

继续扯 C++11,日进一步,亦可赛艇。

原子操作

原子操作,是多线程环境下的一个重要概念,是指它是否在共享内存中完成了一个线程相关的单步操作。

  • 当一个原子存储作用于一个共享变量时,其他的线程不能监测到这个未完成的修改值。
  • 当一个原子加载作用于一个共享变量时,它读取到这个完整的值,就像此时出现了一个单独的时刻。

而非原子加载和存储则不能做到上述两点保证。

为了保证数据的最终一致性,我们可以定义如下规则:

任何时刻两个线程同时操作一个共享变量,当其中一个为写操作时,这两个线程必须使用原子操作。

atomic_flag

atomic_flag 是一种简单的原子布尔类型,只支持两种操作:test-and-setclear

构造函数

atomic_flag 只有默认构造函数,拷贝构造函数已被禁用,因此不能从其他的 atomic_flag 对象构造。

atomic_flag() noexcept = default;
atomic_flag (const atomic_flag&T) = delete;

如果在初始化时没有明确使用 ATOMIC_FLAG_INIT 初始化,那么新创建的 atomic_flag 对象的状态是未指定的(unspecified)(既没有被 set 也没有被 clear。)另外,atomic_flag 不能被拷贝,也不能 move 赋值。

ATOMIC_FLAG_INIT:

如果某个 atomic_flag 对象使用该宏初始化,那么可以保证该对象在创建时处于 clear 状态。

看一个简单的例子,main() 函数中创建了 10 个线程进行计数,率先完成计数任务的线程输出自己的 ID,后续完成计数任务的线程不会输出自身 ID:

#include <iostream>  
#include <atomic>
#include <thread>      
#include <vector>  

// can be checked without being set
std::atomic<bool> ready (false);   

// always set when checked
std::atomic_flag winner = ATOMIC_FLAG_INIT;  

void count1m (int id) {
  // wait for the ready signal
  while (!ready) { std::this_thread::yield(); } 

  // go!, count to 1 million
  for (int i=0; i<1000000; ++i) {}     

  if (!winner.test_and_set()) { 
      std::cout << "thread #" << id << " won!\n"; 
  }
};

int main ()
{
  std::vector<std::thread> threads;

  std::cout << "spawning 10 threads that count to 1 million...\n";
  for (int i=1; i<=10; ++i) 
      threads.push_back(std::thread(count1m,i));

  ready = true;
  for (auto& th : threads) 
      th.join();

  return 0;
}

test_and_set

test_and_set() 函数检查 atomic_flag 标志:

如果之前没有被设置过,则设置 atomic_flag 的标志,并返回 false

如果之前 atomic_flag 对象已被设置,则返回 true

bool test_and_set (memory_order sync = memory_order_seq_cst) volatile noexcept;
bool test_and_set (memory_order sync = memory_order_seq_cst) noexcept;

test-and-set 操作是原子(read-modify-write)的,它到当前操作不受其他线程影响。

参数 sync 的取值如下:

Memory Order 值 Memory Order 类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_release Release
memory_order_acq_rel Acquire/Release
memory_order_seq_cst Sequentially consistent

这里涉及到有关 C++ 中内存模型 Memory Order 姿势,先挖个坑,日后单写一篇这个主题的文章。

clear

清除 atomic_flag 对象的标志位,设置 atomic_flag 的值为 false

void clear (memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear (memory_order sync = memory_order_seq_cst) noexcept;

清除标志使得下一次调用 test_and_set 返回 false

自旋锁

结合 test_and_set()clear()atomic_flag 对象可以当作一个简单的自旋锁 使用,请看下例:

#include <iostream>
#include <atomic> 
#include <thread> 
#include <vector> 
#include <sstream> 

std::atomic_flag lock_stream = ATOMIC_FLAG_INIT;
std::stringstream stream;

void append_number(int x) {
  while (lock_stream.test_and_set())  // acquire lock
          ; //spin
  stream << "thread #" << x << '\n';
  lock_stream.clear();                // release lock
}

int main ()
{
  std::vector<std::thread> threads;

  for (int i=1; i<=10; ++i)
      threads.push_back(std::thread(append_number,i));

  for (auto& th : threads) 
      th.join();

  std::cout << stream.str();
  return 0;
}
 
我为朝露谁苦多 更多文章 写给女儿 从「王宝强事件」学人生经验 平常心造就一颗赛艇 公众号最后一日:一场重命名风波 女排夺冠困扰我的两大难题
猜您喜欢 Docker - 原理,技巧与使用指南 12个鲜为人知的CSS技能(上) “疯狂的木马”截取验证码短信盗刷套现 Performanced C++ 经验规则(1):你不知道的构造函数(上) RESTful 安卓网络层解决方案(一):概览与认证实现方案