C++11 多线程相关的头文件
- C++ 新标准中引入了四个头文件来支持多线程编程,他们分别是 < atomic>, < thread>, < condition_variable> 和< future>.
- < atomic>:该头文件主要声明了两个类,std::atomic和std::atomic_flag,另外还声明了一套C分格的原子类型和与C兼容的原操作的函数.
- < thread>:该头文件主要声明了std::thread类,另外std::this_thread命名空间也在该头文件中.
- < mutex>:该头文件主要申明了与互斥量(mutex)相关的类,包括std::mutex系列类,std::lock_guard,std::unique_lock,以及其他类型和函数.
- :该头文件主要声明了与条件变量相关的类,包括std::condition_variable和std::condition_variable_any.
- < future>:该头文件主要申明了std::promise,std::package_task两个Provider类,以及std::future和std::share_future两个Future类,另外还有一些与之相关的类型和函数,std::async()函数就声明在此文件中.
std::thread构造函数
- default thread() noexcept; //noexcept不会抛出异常,默认构造函数,创建一个空的thread执行对象.
- initialization template
move赋值操作
- move(1) thread& operator=(thread& rhs) noexcept;
- copy delete thread operator(const thread &)=delete;
(1)move 赋值操作,如果当前对象不可joinable,需要传递一个右值引用(rhs)给move赋值操作,如果当前对象可被joinable,则terminate()报错.
(2)拷贝赋值操作被禁用,thread 对象不可被拷贝。
线程调用到的函数在一个类中,那必须将该函数申明为静态函数,因为静态成员函数属于静态全局区,线程可以共享这个区域,故可以各自调用.
< mutex>头文件介绍
Mutex系列类(4种)
- std::mutex,最基本的Mutex类.
- std::recursive_mutex,递归Mutex类.
- std::time_mutex,定时Mutex类.
- std::recursive_timed_mutex,定时递归Mutex类.
Lock类(2种)
- std::lock_guard,与Mutex RAII相关,方便线程对互斥量上锁.
- std::unique_lock,与Mutex RAII相关,方便线程对互斥量上锁,但提供了更好的上锁和解锁控制.
其他类型
- std::once_flag
- std::adopt_lock_t
- std::defer_lock_t
- std::try_to_lock_t
函数
- std::try_lock,尝试同时对多个互斥量上锁.
- std::lock,可以同时对多个互斥量上锁.
- std::call_once,如果多个线程需要同时调用某个函数,call_once可以保证多个线程对该函数只调用一次.
std::mutex的成员函数
- 构造函数,std::mutex不允许拷贝构造,也不允许move拷贝,最初产生的mutex对象是处于unlockeed状态的.
- lock(),调用线程将锁住该互斥量,线程调用该函数会发生下面三种情况:(1)如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用unlock之前,该线程一直拥有该锁,(2)如果当前的互斥量被其他线程锁住,则当前的调用线程被阻塞.(3)如果当前互斥量被调用线程锁住,则会产生死锁.
- unlock(),解锁,释放对互斥量的所有权.
- try_lock(),尝试锁住互斥量如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该函数也会出现下面 3 种情况(1). 如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock 释放互斥量。(2). 如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。(3).如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
几个与锁类型相关的Tag类,分别如下:
- std::adopt_lock_t,一个空的标记类,定义如下:struct adopt_lock_t{};该类型的常量对象adopt_lock(adopt_lock是一个常量对象,定义如下:constexpr adopt_lock_t adopt_lock{};通常作为参数传给unique_lock或lock_guard的构造函数.
- std::defer_lock_t,一个空的标记类,定义如下:struct defer_lock_t{}; 该类型的常量对象defer_lock (defer_lock 是一个常量对象,定义如下: constexpr defer_lock_t defer_lock{}; 通常作为参数传给unique_lock或lock_guard的构造函数.
- std::try_to_lock_t,一个空的标记类,定义如下: struct try_to_lock_t {}; 该类型常量对象try_to_lock(try_to_lock是一个常量对象,定义如下:constexpr try_to_lock_t try_to_lock{},通常作为参数传给unique_lock或lock_guard的构造函数.
std::lock_guard介绍
std::lock_gurad 是 C++11 中定义的模板类。定义如下:
template < class Mutex> class lock_guard;
lock_guard对象通常用于管理某个锁(Lock)对象,在某个lock_guard对象的声明周期内,它所管理的锁对象会一直保持上锁状态,而lock_guar=生命周期结束后,它所管理的锁对象会被解锁. 模板Mutex代表互斥量类型,例如std::mutex类型,它应该是一个基本的BasicLockable类型
* 在 lock_guard 对象构造时,传入的 Mutex 对象(即它所管理的 Mutex 对象)会被当前线程锁住。在lock_guard 对象被析构时,它所管理的 Mutex 对象会自动解锁,由于不需要程序员手动调用 lock 和 unlock 对 Mutex 进行上锁和解锁操作,因此这也是最简单安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。
lock_guard 构造函数
locking(1) explicit lock_guard(mutex_type& m);
adopting(2) lock_guard(mutex_type& m,adopt_lock_t tag);
copy deleted lock_guard(const lock_guard&)=delete;
1.locking初始化:
lock_guard 对象管理Mutex对象m,并在构造时对m进行上锁(调用m.lock()).
2.adopting初始化
lock_guard对象管理Mutex对象m,与locking初始化(1)不同的是,Mutex对象m已被当前线程锁住.
3.拷贝构造
lock_guard对象的拷贝构造和移动构造都被禁用,因此lock_guard对象不可被拷贝构造或移动构造.
std::unique_lock介绍
- unique_lock 对象以独占所有权的方式管理 mutex 对象的上锁和解锁操作就是没有其他的 unique_lock 对象同时拥有某个 mutex 对象的所有权。
- 在构造(或移动(move)赋值)时,unique_lock 对象需要传递一个 Mutex 对象作为它的参数,新创建的 unique_lock 对象负责传入的 Mutex 对象的上锁和解锁操作.
unique_lock 不同于lock_guard的用法.
unique_lock的构造函数
- default(1) unique_lock noexcept;
- locking(2) explicit unique_lock(mutex_type & m);
- try-locking(3) unique_lock(mutex_type& m,try_to_lock_t tag);
- deferred(4) unique_lock(mutex_tyoe& m,defer_lock_t tag) noexcept;
- adopting(5) unique_locking(mutex_type& m,adopt_lock_t tag);
- locking for(6) template
unique_lock移动赋值操作
- move(1) unique_lock& operator=(unique_lock&& x)noexcept;
- copy deleted unique_lock& operator=(const unique_lock&)=delete;
- 移动赋值之后,由x所管理的Mutex对象及其状态将会被新的unique_lock对象取代.
- 如果被赋值的对象之前已经获得了它所管理的Mutex对象的锁,则在移动赋值之前会调用unlock函数释放它所占有的锁.
- 调用移动赋值之后,x对象如同通过默认构造函数所创建的,也就不再管理任何Mutex对象了. ### unique_lock主要成员函数
(1)上锁/解锁操作:lock,try_lock,try_lock_for,try_lock_until和unlock.
(2)修改操作:移动赋值,交换(swap)(与另外一个unique_lock对象交换他们所管理的Mutex对象的所有权),释放(release)(返回指向它所管理的Mutex对象的指针).
(3)获取属性操作:owns_lock(返回当前unique_lock对象是否获得了锁),operator bool()(与owns_lock功能相同,返回当前unique_lock对象是否获得了锁,mutex(返回当前unique_lock对象所管理的Mutex对象的指针).
unique_lock::lock
该函数返回时,当前的unique_lock对象便拥有了它所管理的Mutex对象的锁,如果上锁失败,则抛出system_error异常.
unique_lock::try_lock
上锁操作,调用它所管理的Mutex对象的try_lock函数,如果上锁成功,返回true,否则返回false.
std::unique_lock::try_lock_for
上锁操作,调用它所管理的Mutex对象的try_lock_for函数,上锁成功,返回true,否则返回false.
std::unique_lock::release
返回指向所管理的Mutex对象的指针,并释放所有权.
std::unique_lock::owns_lock
返回当前std::unique_lock对象是否获得了锁.
头文件介绍
- Provides类:std::promise,std::package_task
- Futures类:std::future,shared_future.
- Provides函数:std::async()
- 其他类型:std::future_error,std::future_errc,std::future_status,std::launch.
std::promise类介绍
- promise对象可以保存某一类型T的值,该值可以被future对象读取(可能在另外一个线程中),因此promise也提供了一种线程同步的手段,在promise对象构造时可以和一个共享状态(通常是std::future)相关联,并可一在相关联的共享状态(std::future)上保存一个类型为T的值.
- 可以通过get_future来获取与该promise对象相关联的future对象,调用该函数之后,两个对象共享相同的共享状态.
- promise对象是异步Provider,它可以在某一时刻设置共享状态的值.
- future对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志为ready,然后才能获取共享状态的值 .
std::promise构造函数
- default(1) promise();
- with allocator(2) template promise(allocator_arg_t aa,const Alloc& alloc);
- copy deleted promise(const promise&)=delete;
- move(4) promise(promise&& x) noexcept;
(1)默认构造函数,初始化一个空的共享状态,
(2)带自定义内存分配器的构造函数,与默认构造函数类似,使用自定义分配器来分配共享状态
(3)拷贝构造函数,被禁用.
(4)移动构造函数.
std::promise::get_future介绍
- 该函数返回一个与promise共享状态相关联的future,返回的future对象可以访问由promise对象设置在共享状态上的值或者某个异常对象,只能从promise共享状态获取一个future对象,在调用该函数之后,promise对象通常会在某个时间点准备好(设置一个值或者一个异常对象),如果不设置值或异常,promise对象在析构时会自动设置一个future_error异常.
- std::promise::set_value介绍
generic template(1) void set_value(const T& val); void set_value(T&& val);
specializations(2) void promise
std::promise::set_value_at_thread_exit 介绍
*设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready。如果某个 std::future 对象与该 promise 对象的共享状态相关联,并且该 future 正在调用 get,则调用 get 的线程会被阻塞,当线程退出时,调用 future::get 的线程解除阻塞,同时 get 返回 set_value_at_thread_exit 所设置的值。注意,该函数已经设置了 promise 共享状态的值,如果在线程结束之前有其他设置或者修改共享状态的值的操作,则会抛出 future_error( promise_already_satisfied )。
std::promise::swap介绍
交换promise的共享状态.
std::package_task
- std::packaged_task**包装一个可调用的对象**,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,std::package_task与std::function类似,只不过std::packaged_task将其包装的可调用对象的执行结果传递给一个std::future对象(该对象通常在另外一个线程中获取std::packaged_task任务的执行结果).
- std::packaged_task对象内部包含两个最基本元素,一.被包装的任务,任务是一个可调用的对象,如函数指针,成员函数指针或函数对象,二.共享状态(shared state),用于保存任务的返回值,可以通过std::future对象来达到异步访问共享状态的效果.
- 可以通过std::packsged_task::get_future来获取与共享状态相关联的std::future对象,在调用该函数之后,两个对象共享相同的共享状态:
- std::packaged_task对象是异步Provider,它是某一时刻通过调用被包装的任务来设置共享状态的值.
- std::future对象是一个异步返回对象,通过它可以获得共享状态的值,当然在必要的时候需要等待共享状态变为ready;
- std::packaged_task的共享状态的生命周期一直持续到最后一个与之关联的对象被释放或者销毁为止.
std::packaged_task 构造函数
default(1) package_task() noexcept;
initialization(2) template explicit packaged_task (Fn &&fn);
with allocator(3) template
std::packaged_task::valid 介绍
检查当前packaged_task是否和一个有效的共享状态相关联,对于由默认构造函数生成的package_task对象,该函数返回false,除非中间进行了move赋值操作或者swap操作.
std::packaged_task::operator()(Args…..args)介绍
- 调用该packaged_task对象所包装的对象(通常为函数指针,函数对象,lambda表达式等),传入的参数为args,调用该函数一般会发生两种情况:
- 如果成功调用packaged_task所包装的对象,则返回值(如果包装的对象有返回值的话)被保存在packaged_task的共享状态中.
- 如果调用package_task所包装的对象失败,并且抛出了异常,则异常也会被保存在packaged_task的共享状态中,调用失败,并且抛出了异常,则异常也会被保存在package_task的共享状态中.
std::packaged_task::make_ready_at_thread_exit 介绍
- 该函数会调用被包装的任务,并向任务传递参数,类似 std::packaged_task 的 operator() 成员函数。但是与 operator() 函数不同的是,make_ready_at_thread_exit 并不会立即设置共享状态的标志为 ready,而是在线程退出时设置共享状态的标志。
- 如果与该 packaged_task 共享状态相关联的 future 对象在 future::get 处等待,则当前的 future::get 调用会被阻塞,直到线程退出。而一旦线程退出,future::get 调用继续执行,或者抛出异常。
std::package_task::reset()介绍
重置packaged_task的共享状态,但是保留之前的被包装的任务.使得packaged_task 对象被重用.
std::packaged_task::swap() 介绍
交换 packaged_task 的共享状态。
future介绍
- std::future可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段. future通常由某个Provider创建,我们可以把Provider想象成一个异步任务的提供者,Provider在某个线程中设置共享状态的值,与该共享状态相关联的future对象调用get(通常在另一个线程中)获取该值,如果当前的共享状态部位ready,则std::future::get会阻塞当前的调用线程.直到Provider设置了当前共享状态的值,get方法返回异步任务的值或异常.
- 一个有效的std::future对象通常由以下三种Provider创建,并和某个共享状态相关联,Provider可以是函数,也可以是类:
- std::async()函数.
- std::promise::get_future,此时get_future为promise类的成员函数.
- std::packaged_task,此时get_future为package_task类的成员函数.
- 一个 std::future 对象只有在有效(valid)的情况下才有用(useful),由 std::future 默认构造函数创建的 future 对象不是有效的(除非当前非有效的 future 对象被 move 赋值另一个有效的 future 对象)。
std::future构造函数
- std::future一般由std::async,std::promise::get_future,std::packaed_task::get_future创建,不过也提供了构造函数:
- default(1) future() noexcept;
- copydeleted future(const future&)=delete;
- move(3) future(future&& y)noexcept;
std::future<int> fut; //默认构造函数
fut=std::async(do_some_task);
std::future::share()
返回一个std::shared_future对象(本文后续内容将介绍std::shared_future);调用该函数之后,该std::future对象本身已经不和任何共享状态相关联,因此该std::future的状态不再是valid的了.
std::future::get()
std::future::get一共有三种形式:
(1) generic template(1) T get(); 返回一个值.
(2) reference specialization R& future
std::future::valid()
- 检查当前的std::future对象是否有效,即释放与某个共享状态相关联,一个有效的std::future对象只能通过std::async(),std::promise::get_future或者std::packaged_task::get_future来初始化.由std::future默认构造函数创建的std::future对象是无效的,通std::future的move赋值后该std::future对象也可以变为valid.
std::future::wait()
- 等待与当前std::future对象相关联的共享状态的标志变为ready;
- 如果共享状态的标志不是ready(此时Provider没有在共享状态上设置值或者异常),调用该函数会被阻塞当前线程,直到共享状态的标志变为ready,一旦共享状态变为ready,wait()函数返回,当前线程被解除阻塞,但是wait()并不读取共享状态的值或者异常;
std::future::wait_for()
与std::future::wait()的功能类似,即等待与该std::future对象相关联的共享状态变为ready,该函数的原型如下:
template<class Rep,class Period> future_status wait_for(const chrono::duration<Rep,Period>& rel_time)const;
而与std::future::wait()不同的是,wait_for()可以设置一个时间段rel_time,如果共享的标志在该时间段结束之前没有被Provider设置为ready,则调用wait_for的线程被阻塞,在等待了rel_time的时间长度后wait_until()返回.
* future_status::ready:共享的状态已经变为ready,即Provider在共享状态上设置了值或异常.
* future_status::timeout:超时,即在规定的时间内共享状态的标志没有变为ready.
* future_status::deffered:共享状态包含一个deferred函数.
std::future::wait_until()
wait_until()可以设置一个系统绝对时间,如果共享状态的标志在该时间点到来之前没有被Provider设置为ready,则调用wait_until的线程阻塞. ### std::shared_future介绍
std::sahred_future与std::future类似但是std::shared_future可以拷贝,多个std::shared_future可以共享某个共享状态的最终结果(即共享状态的某个值或者异常),shared_future可以通过某个std::future对象隐式转换(std::shared_future的构造函数 ),或者通过future::shared()显式转换,无论哪种转换,被转换的那个std::future对象都会变为not-valid.
std::shared_future构造函数.
(1) default(1) shared_future() noexcept;
(2)copy(2) shared_future(const shared_future& x);
(3)move(3) shared_future(shared_future&& x)noexcept;
(4)move from future(4) shared_future(future&& x) noexcept;
最后move form future(4) 即从一个有效的std::future对象构造一个std::shared_future,构造之后std::future对象x变为无效.
std::async()介绍
(1) unspecified policy(1) template
与std::future相关的枚举类介绍
- enum class future_errc;
- enum class future_status;
- enum calss launch;
future_errc类不长用,
std::launch类型
- launch::async:异步任务会在另外一个线程中调用,并通过共享状态返回异步任务的结果.
条件变量头文件里的类和函数介绍.
头文件主要包含了与条件变量相关的类和函数,相关的类包括std::condition_variable和std::condition_variable_any,还有枚举类型std::cv_status,另外还包括函数std::notify_all_thread_exit(),下面介绍以上几种类型.
condition_variable类介绍
- std::condition_variable是条件变量,Linux下使用Pthread库中的pthread_cond_*()函数提供了与条件变量相关的功能.
- 当std::condition_variable对象的某个wait函数被调用的时候,它使用std::unique_lock(通过std::mutex)来锁住当前线程,当前线程会一直被阻塞,直到另外一个线程在相同的std::condition_variable对象上调用了notification函数来唤醒当前线程.
- std::condition_variable对象通常使用std::unique_lock来等待,如果需要使用另外的lockable类型,可以使用std::condition_variable_any类.
std::condition_variable构造函数
default(1) condtion_variable();
copydeleted condition_variable(const condition_variable&)=delete;
condition_variable的拷贝构造函数被禁用,只提供了默认构造函数.
std::condition_variable::wait()介绍
- (绝对的)uncondition(1) void wait(unique_lock& lck);
- predicate(2) template void wait(unique_lock& lck,Predicate pred);
- std::condition_variable提供了两种wait()函数,当前线程调用wait()后将被阻塞(此时当前线程应该获得了锁(mutex)),不妨设获得锁lck,直到另外某个线程调用notify_*唤醒当前线程.
- 在线程被阻塞时,该函数会自动调用lck.unlock()释放锁,使得其他被阻塞在锁竞争上的线程得以继续执行,另外,一旦当前线程获得通知(notified,通常是另外某个线程调用notify_*唤醒当前线程),wait()函数也是自动调用lck.lock(),使得lck的状态和wait函数被调用时相同.
- 在第二种情况下(即设置了Predicate),只有当pred条件为fasle时调用wait()才会阻塞当前线程,并且在收到其他线程的通知后只有当pred为true时才会被解除阻塞,因此第二种情况类似以下代码.
while(!pred()) wait(lck);
std::condition_variable::wait_for()介绍
与std::condition_variable::wait()类似,不过wait_for可以指定一个时间段,在当前线程收到通知或者指定的时间rel_time超时之前,该线程都会处于阻塞状态,而一旦超时或者收到里其他线程的通知,wait_for返回,剩下的处理步骤和wait()类似.
std::condition_variable::wait_until()介绍
与wait_for()类似,不过wait_until可以指定一个时间点.
std::condition_variable::notify_one()介绍
唤醒某个等待线程,如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的.
std::condition_variable::notify_all()介绍
唤醒所有等待的线程,如果没有等待的线程,则不做什么.
std::condition_variable_any 介绍
与std::condition_variable类似,只不过std::condition_variable_any的wait函数可以接受任何lockable参数.而std::condition_variable只能接受std::unique_lock类型的参数
cv_status枚举类型介绍
- cv_status::no_timeout wait_for或者wait_until没有超时,即在规定的时间内线程接受到通知.
- cv_status::timeout wait_for或者wait_until超时.
notify_all_at_thread_exit .
void notify_all_at_thread_exit(condition_variable& cond,unique_lock<mutex> lck);
当调用该函数的线程退出时,所有在cond条件变量上等待的线程都会收到通知.
atomic类型详解–atomic_flag介绍
- 头文件中最简单的原子类型:atomic_flag,atomic_flag一种简单的原子布尔类型,只支持两种操作,test-and-set和clear.
std::atomic_flag构造函数
atomic_flag() noexcept=default;
atomic_flag(const atomic_flag& T)=delete;
- std::atomic_flag只有默认构造函数,拷贝构造函数已被禁用,因此不能从其他std::atomic_flag对象构造一个新的std::atomic_flag对象.
- 如果在初始化时没有明确使用ATOMIC_FLAG_INIT初始化,那么新创建的std::atomic_flag对象的状态是未指定的(既没有被set也没有被clear),另外,atomic_flag不能被拷贝,也不能move赋值.
- ATOMIC_FLAG_INIT:如果某个std::atomic_flag对象使用该宏初始化,那么可以保证该std::atomic_flag对象在创建时处于clear状态.
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);
- test_and_set()函数检查std::atomic_flag标志,如果std::atomic_flag之前没有被设置过,则设置std::atomic_flag的标志,并返回先前该std::atomic_flag对象是否被设置过.如果已经被设置过,则返回true,否则返回false.
test_and_set参数sync的取值.
Memory Order值 | Memory Order类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_consume | Consume |
memory_order_acquire | Acquire |
memeory_order_release | Release |
memory_order_acq_rel | Acquire/Release |
memory_order_seq_cst | Sequentially consistent |
std::atomic_flag::clear()介绍
清除std::atomic_flag对象的标志位,即设置atomic_flag的值为false,clear函数的原型如下:
void clear(memory_order sync = memory_order_seq_cst) volatile noexcept;
void clear(memory_order sync = memory_order_seq_cst) noexcept;
清除std::atomic_flag标志使得下一次调用std::atomic_flag::test_and_set返回false;
std::atomic介绍
std::atomic是模板类,一个模板类型为T的原子对象中封装了一个类型为T的值.
template<class T> struct
atomic;
原子类型对象的主要特点就是从不同线程访问不会导致数据竞争. 因此从不同线程访问某个原子对象是良性行为,而通常对于非原子类型而言,并发的访问某个对象(如果不做任何同步操作)会导致未定义的行为发生. std::atomic成员函数
std::atmoic的构造函数如下:
default(1) atmoic() noexcept=default;
initialization(2) constexpr atomic(T val) noexcept;
copydeleted atomic(const atomic&)=delete;
1.默认构造函数,由默认构造函数创建的std::atomic对象处于未初始化状态,对于为初始化的std::atomic对象可以由atomic_init函数进行初始化.
2.初始化构造函数,由类型T初始化一个std::atomic对象.
3.拷贝构造函数被禁用.
std::atomic::operator=()函数
std::atomic的赋值操作函数定义如下:
set value(1) T operator=(T val) noexcept;
T operator=(T val)volatile noexcept;
copydeleted;
普通的赋值拷贝操作已经被禁用,但是一个类型为T的变量可以赋值给相应的原子类型变量(相当于隐式转换),该操作是原子的,内存序默认顺序一致性(memory_order_seq_cst),如果需要指定其他的内存序,需要使用std::atomic::store();
is_lock_free
bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;
判断std::atomic对象是否具备lock-free的特性,如果某个对象满足lock-free特性,在多个线程访问该对象时不会导致线程阻塞,
store
函数原型:
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; 修改被封装的值,std::atomic::store函数将类型为T的参数val复制给原子对象所封装的值,T是std::atomic类模板参数,另外参数sync指定内存序.
Memory Order 值 | Memory Order类型 |
---|---|
memory_order_relaxed | Relaxed |
memory_order_release | Release |
memory_order_seq_cst | Sequentially consistent |
load
T load(memory_order sync=memory_order_seq_cst) const volatile noexcept;
T load(memory_order sync=memory_order_seq_cst) cosnt noexcept;
operatorT() const volatile noexcept;
operator T()const noexcept;
与load功能类似,也是读取被封装的值,operator T()是类型转换操作,默认的内存序是std::memory_order_seq_cst,如果需要指定其他的内存序,应该使用load()函数;
exchange
T exchange(T val,memory_order sync = memory_order_seq_cst) volatile noexcept;
T exchange(T val,memory_order_sync = memory_order sync =memory_order_seq_cst) noexcept;
读取并修改被封装的值,exchange会将val指定的值替换掉之前该原子对象封装的值,并返回之前该原子对象封装的值,整个过程是原子的.
内存序(简单介绍一下,前面用到了,知道内存序是为了约束内存操作的就行,具体的内存模型以后再学习)
c++11中中定义了以下6种语义来对内存操作的行为进行约束,这些语义分别规定了不同的内存操作在其他线程中的可见性问题.
enum memory_oeder{
memory_order_relaxed, //保证是原子操作,但是不保证优化后的代码顺序.
memory_order_consume, //与操作数相关的才会有效.
memory_order_acquire, //不仅是从内存读,还保证了内存代码执行的顺序,这句代码必须在这句代码后面的代码执行前执行.
memory_order_release, //确保写入内存,但同时也保证了:这句代码必须在执行完这句代码前面的代码之后执行.
memory_order_acq_rel, //看一下exchange吧,相当于acquire和release;
memory_order_seq_cst //不允许编译器调整语句顺序,
};