首页 > 编程语言 >C++多线程:atomic

C++多线程:atomic

时间:2024-11-01 20:24:31浏览次数:1  
标签:std include noexcept C++ atomic memory 多线程 order

在许多为了性能和效率的场景下,需要开发一些lock-free的算法和数据结构

atomic_flag

原子布尔类型,只支持 test-and-set 和 clear 操作

构造函数

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

只有默认构造函数,而不能从其他对象构造
atomic_flag对象需要使用ATOMIC_FLAG_INIT初始化,使其处于 clear 状态,否则是unspecified未指定的

创建10个线程用于计数,先完成计数任务的线程输出id

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

std::atomic<bool> ready(false);
std::atomic_flag winner=ATOMIC_FLAG_INIT;

void count1m(int id){
  while(!ready){
    std::this_thread::yield();
  }
  for(int i=0; i<10000; ++i){
    // count
  }
  // 执行完后,该函数
  if(!winner.test_and_set()){
    std::cout<<"thread "<<id<<" first\n";
  }
};

int main(){
  std::vector<std::thread> ths;
  std::cout<<"spawning 10 threads that count to 1 million..."<<std::endl;

  for(int i=1; i<=10; ++i){
    ths.push_back(std::thread(count1m, i));
  }
  ready= true;

  for(auto& th:ths){
    th.join();
  }

  return 0;
}

std::atomic_flag::test_and_set

函数原型如下:

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;

用于检查标志,若std::atomic_flag之前被设置过,则返回true,否则返回false并设置标志
该操作为原子操作,可以指定的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
#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()){}
  stream<<"thread "<<x<<'\n';
  lock_stream.clear();
}

int main(){
  std::vector<std::thread> ths;
  for(int i=1; i<=10; ++i){
    ths.push_back(std::thread(append_number,i));
  }
  for(auto& th:ths){
    th.join();
  }
  std::cout<<stream.str()<<std::endl;
  return 0;
}

std::atomic_flag::clear

清除对象标志,即设置为false
函数原型如下:

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

该函数也可以指定Memory Order的值

std::atomic_flag可以当作一个简单的自旋锁使用

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

std::atomic_flag lock= ATOMIC_FLAG_INIT;

void f(int n){
  for(int cnt=0; cnt<100; ++i){
    while(lock.test_and_set(std::memory_order_acquire))
    ; //spin
    std::cout<<"output from thread "<<n<<'\n';
    lock.clear(std::memory_order_release);
  }
}

int main() {
  std::vector<std::thread> ths;
  for(int n=0; n<10; ++n){
    ths.emplace_back(f,n);
  }
  for(auto& th:ths){
    th.join();
  }

  return 0;
}

std::atomic_flag的加锁操作可以理解为lock.test_and_set(std::memory_order_acquire);
加锁操作时,返回false表示加锁成功
因为此前lock的标志为false,调用test_and_set()后标志为true,说明某一线程获得了锁
std::atomic_flag的解锁操作相当于lock.test_and_set(std::memory_order_release);

atomic

原子类型atomic_flag过于简单,下面介绍功能更完善的std::atomic
原子类型对象的特点:不同线程访问不会导致数据竞争data race问题

构造函数

std::atomic是一个模板类

template<class T>
struct atomic;

template<>
struct atomic<integral>{};

template<class T>
struct atomic<T*>{};

标准库提供了对整型和指针类型的特化实现

默认构造函数
atomic()noexcept=default;
对象处于未初始化状态,需要使用atomic_init进行初始化

初始化构造函数
constexpr atomic(T val)noexcept;
可用T对象对atomic进行初始化

拷贝构造函数
atomic(const atomic&)=delete;
被禁用,不可拷贝

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

std::atomic<bool> ready(false);
std::atomic_flag winner= ATOMIC_FLAG_INIT;

void do_count1m(int id){
  while(!ready){
    std::this_thread::yield();
  }
  for(volatile int i=0; i<1000000; ++i){
  }
  if(!winner.test_and_set()){
    std::cout<<"thread "<<id<<" first\n";
  }
}

int main(){
  std::vector<std::thread> ths;
  std::cout<<"spawning 10 threads that count to 1 million...\n";
  for(int i=1; i<=10; ++i){
    ths.push_back(std::thread(count1m,i));
  }
  ready= true;
  for(auto& th:ths){
    th.join();
  }
  return 0;
}

成员函数

赋值操作
std::atomic::operator=()函数

copy (deleted)

atomic& operator=(const atomic&)=delete;
atomic& operator=(const atomic&)volatile=delete;

赋值运算是被禁用的

set value

T operator=(T val)noexcept;
T operator=(T val)volatile noexcept;

重载了赋值运算符,使得原子类型可以被类型为T的变量赋值,类似于隐式转换
该操作是原子操作,内存序默认为顺序一致性std::memory_order_seq_cst
若需要使用其他内存序,可以使用std::atomic::store()

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

std::atomic<int> foo=0;

void set_foo(int x){
  foo=x;
}
void print_foo(){
  while(foo==0){
    std::this_thread::yield();
  }
  std::cout<<"foo:"<<foo<<std::endl;
}
int mian(){
  std::thread first(print_foo);
  std::thread second(set_foo, 10);
  first.join();
  second.join();
  return 0;
}

常用成员函数

bool is_lock_free()const volatile noexcept;
bool is_lock_free()const noexcept;

判断对象是否有lock-free特性
若对象具有该特性,则多线程访问该对象时不会导致线程阻塞

void store(T val, memory_order sync=memory_order_seq_cst)volatile noexcept;
void store(T val, memory_order sync=memory_order_seq_cst)noexcept;

修改被封装的值,将val复制给原子对象所封装的值,sync指定内存序

类型
memory_order_relaxed Relaxed
memory_order_release Release
memory_order_seq_cst Sequentially consistent

示例如下

#include<iostream>
#include<atomic>
#include<thread>
std::atomic<int> foo(0);
void set_foo(int x){
  foo.store(x, std::memory_order_relaxed);
}
void print_foo(){
  int x;
  do{
    x= foo.load(std::memory_order_relaxed);
  }while(x==0);
  std::cout<<"foo:"<<x<<std::endl;
}
int main(){
  std::thread first(print_foo);
  std::thread second(set_foo, 10);
  first.join();
  second.join();
  return 0;
}
T load(memory_order sync=memory_order_seq_cst)const volatile noexcept;
T load(memory_order sync=memory_order_seq_cst)const noexcept;

读取被封装的值,参数sync设置内存序

类型
memory_order_relaxed Relaxed
memory_order_consume Consume
memory_order_acquire Acquire
memory_order_seq_cst Sequentially consistent

示例如下

#include<iostream>
#include<atomic>
#include<thread>
std::atomic<int> foo(0);
void set_foo(int x){
  foo.store(x, std::memory_order_relaxed);
}
void print_foo(){
  int x;
  do{
    x= foo.load(std::memory_order_relaxed);
  }while(x==0);
  std::cout<<"foo:"<<x<<'\n';
}
int main(){
  std::thread first(print_foo);
  std::thread second(set_foo, 10);
  first.join();
  second.join();
  return 0;
}
operator T()const volatile noexcept;
operator T()const noexcept;

load功能类似,读取封装的值,默认内存序是std::memory_order_seq_cst

示例如下

T exchange(T val, memory_order sync=memory_order_seq_cst)volatile noexcept;
T exchange(T val, memory_order sync=memory_order_seq_cst)noexcept;
compare_exchange_weak
compare_exchange_strong

1

std::atomic对整型和指针类型特化

成员函数
fetch_add
fetch_sub
fetch_and
fetch_or
fetch_xor
operator++
operator--

2

C++11原子操作中C风格的API
std::atomic和std::atomic_flag

标签:std,include,noexcept,C++,atomic,memory,多线程,order
From: https://www.cnblogs.com/sgqmax/p/18521181

相关文章

  • C++对象模型:constructor
    构造函数constructorexplicit的引入,是为了能够制止“单一参数的constructor”被当作一个conversion运算符带有默认构造函数的对象成员若一个类中包含对象成员,且该对象有默认构造函数,此时:若该类没有构造函数则编译器会合成一个默认构造函数,且发生在真正调用时若该类有构造函......
  • 【C++】类与对象(中)
    1.类的默认成员函数  默认成员函数就是用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。一个类,我们不写的情况下编译器会默认生成以下6个默认成员函数,需要注意的是这6个中最重要的是前4个,最后两个取地址重载不重要,我们稍微了解⼀下即可。其次就是C++11以后......
  • C++多线程:thread
    进程与线程进程:系统资源分配的最小单元,通常被定义为一个正在运行的程序实例线程:系统任务调度的最小单元进程间通信:管道,信号量,信号,消息队列,共享内存,套接字线程间通信:锁机制,信号量机制,信号机制,屏障同步:保证任务片段的先后顺序互斥:为了保证资源在同一时刻只能被一个线程使用,即......
  • C++多线程:mutex
    互斥量C++11互斥锁定义在<mutex>头文件中,提供了独占资源的特性C++11头文件中定义的互斥量互斥量说明mutex基本互斥量recursive_mutex递归互斥量timed_mutex定时互斥量recursive_timed_mutex递归定时互斥量std::mutex最基本的互斥量,提供了独占所有权......
  • C++多线程:condition_variable
    条件变量类似于pthread库中的pthread_cond_*()提供的功能,C++11标准提供了两种表示条件变量的类,分别是condition_variable和condition_variable_any,定义在头文件<condition_variable>中std::condition_variable当std::condition_variable对象调用wait()时,会阻塞当前线程,直到该s......
  • C++多线程:promise
    头文件包含:Providers类std::promisestd::packaged_taskFutures类std::futurestd::shared_futureProviders函数std::async()其他类型std::future_errorstd::future_errcstd::future_statusstd::launchstd::promise用来保存某一类型T的值,该值可以被future对......
  • C++多线程:package_task
    std::packaged_taskstd::packaged_task包装一个可调用对象,并允许获取该可调用对象计算的结果,可调用对象内部包含两个基本元素:1.被包装的任务任务是一个可调用对象,如函数指针或函数对象,该对象的执行结果会传递给共享状态2.共享状态用于保存任务的返回结果,并可通过future对象异......
  • C++标准库:chrono
    ratio先看一下ratio的定义template<intmax_tN,intmax_tD=1>classratio;ratio是由非类型参数定义的模板,用来定义一个比率N/D,如ratio<1,-2>表示-0.5标准库中定义的常用ratio类型typedefinition说明ratio<1,1000>std::milli1/1000ratio<1,1000000>std::mic......
  • C++标准库:random
    随机数生成设备随机数生成设备random_device,生成非确定性随机数,在Linux中通过读取/dev/urandom设备,Windows中使用rand_s重载了()运算符,每次调用会生成一个min()到max()之间的高质量随机数种子,若在Linux(UnixLike)下,可以使用这个生成高质量的随机数,可以理解为真随机数,windows下......
  • Chromium 中chrome.topSites扩展接口定义c++
    一、chrome.topSites使用 chrome.topSites API访问新标签页上显示的热门网站(即最常访问的网站)。不包括用户自定义的快捷方式。权限topSites您必须声明“topSites”扩展程序清单中授予使用此API的权限。{ "name":"Myextension", ... "permissions":[ ......