本文章所有内容源于《BOOST程序库完全开发指南:深入C++“准”标准库(第3版)》第十二章
本章内容包括Boost库中的三个并发编程组件。atomic,它实现了C++11标准定义的原子操作库;thread,它兼容了C++11标准,为C++增加了可移植的线程处理能力;asio,用于同步和异步IO操作的功能强大的库,使用前摄器模式,可以处理口、网络通信,而且有望成为C++标准底层通信库。
1. atomic
1.1 功能:
本节代码都是单线程的,没有并发。
1.2 头文件:
#include<boost/atomic.hpp>
using namespace boost;
1.3 用法:
#include "/home/fjc/桌面/算法书/C++Boost/boost_guide/common/std.hpp"
using namespace std;
#include <boost/atomic.hpp>
using namespace boost;
//
void case1()
{
atomic<int> a(10); //记住这里的构造方法
assert(a == 10);
//不要缺省构造很危险:atomic<int> a;
atomic<double> l;
l = 100L;
cout << l << endl;
atomic<double> d(2.414);
cout << d << endl;
cout << "--------------"<<endl;
}
void case2()
{
atomic<bool> b{false}; //原子化bool
assert(!b.load()); //显示调用load取值
b.store(true); //显示调用store
assert(b); //隐士类型转换,等价于load
atomic<int> n(100); //原子化int
assert(n.exchange(200) == 100); //存储的同时返回原始值
assert(n == 200); //隐式类型转换,等价于load
cout << "--------------"<<endl;
}
//
void case3()
{
atomic<long> l(100);
long v = 100; //设置变量expected,左值
if (l.compare_exchange_weak(v, 313)) //比较并交换
{
assert(l == 313 && v == 100); //如果成功值改变,输出原值100
}
v = 200; //设置变量expected=200
auto b = l.compare_exchange_strong(v, 99); //比较并交换
assert(!b && v == 313); //交换失败,输出原值313
l.compare_exchange_weak(v, 99); //再次比较交换
assert(l == 99 && v == 313 );
cout << "--------------"<<endl;
}
//
#include <boost/utility.hpp>
void case4()
{
atomic<int> n(100);
assert(n.fetch_add(10) == 100);
assert(n == 110);
assert(++n == 111);
assert(n++ ==111);
assert(n == 112);
assert((n -= 10) == 102);
atomic<int> b{BOOST_BINARY(1101)}; //二进制1101
auto x = b.fetch_and(BOOST_BINARY(0110)); //逻辑与运算,返回原始数值1101
assert(x == BOOST_BINARY(1101) &&
b == BOOST_BINARY(0100)); //b运算后是0100
assert((b |= BOOST_BINARY(1001)) //相当于fetch_or,返回运算后数值
== BOOST_BINARY(1101));
}
//
void case5()
{
atomic<bool> b{true};
assert(b);
b = false;
assert(!b.load());
auto x = b.exchange(true);
assert(b && !x);
}
//
#include <boost/intrusive_ptr.hpp>
template<typename T>
class ref_count //泛型引用计数类
{
private:
typedef boost::atomic<int> atomic_type; //定义atomic类型
mutable atomic_type m_count{0}; //初始化注意是mutable
protected:
ref_count() {}
~ref_count() {}
public:
typedef boost::intrusive_ptr<T> counted_ptr;
void add_ref() const //增加引用计数
{
m_count.fetch_add(1, boost::memory_order_relaxed); //不做任何顺序要求
}
void sub_ref() const //减少引用计数
{
if (m_count.fetch_sub(1, boost::memory_order_release) == 1)
{
boost::atomic_thread_fence(boost::memory_order_acquire);//原子级别线程保护,获取之前的修改
delete static_cast<const T*>(this); //删除指针,需要转型
}
}
decltype(m_count.load()) count() const //获取引用计数,注意decltype
{
return m_count.load(); //取值也可以用隐士类型转换
}
public:
template<typename ... Args> //可变参数模板
static counted_ptr make_ptr(Args&& ... args) //工厂函数
{
return counted_ptr(new T(std::forward<Args>(args)...));
}
private:
friend void intrusive_ptr_add_ref(const T* p) //要求函数
{
p->add_ref();
}
friend void intrusive_ptr_release(const T* p) //要求函数
{
p->sub_ref();
}
};
class demo: public ref_count<demo> //添加引用计数能力
{
public:
demo()
{
cout << "demo ctor" << endl;
}
~demo()
{
cout << "demo dtor" << endl;
}
int x;
};
void case6()
{
//demo::counted_ptr p(new demo);
auto p = demo::make_ptr();
p->x = 10;
assert(p->x == 10);
assert(p->count() == 1);
}
//
int main()
{
case1();
case2();
case3();
case4();
case5();
case6();
}
2. thread
2.1 功能:
需要chrono库提供的时间概念来执行水面、等待操作,需要先编译chrono库
2.2 头文件:
#include<boost/thread.hpp>
using namespace boost;
2.3 用法:
2.3.1 mutex:
mutex:
互斥量(mutex)是一种用于线程同步的手段,可以防止多个线程同时操作共享资源。一旦一个线程锁住了互斥量,那么其他线程必须等待它解锁互斥量后才能再访问共享资源
timed_mutex:
如果不想因为mutex而阻塞线程,那么就可以使用timed_mutex,调用它的try_lock_for()或try_lock_until(),等待一个相对或者绝对时间
2.3.2 lock_guard:
用于辅助锁定互斥量。构造时锁定,析构时解锁,避免遗忘解锁,像是一个智能指针
2.3.3 unique_lock:
与lock_guard类似,它的构造函数可以接收其他的锁定选项,从而有不同的行为。unique_lock不允许拷贝,但它内部使用指针而不是引用来保存互斥量,这使得它可以被转移,用起来更灵活。
2.3.4 lock适配器:
lock_guard、unique_lock通常搭配mutex使用。适配器类需要在模板参数里指定要适配的mutex类型,以继承的方式使用,子类自动获得他们的lock接口,然后就可以用locak_guard和unique_lock。
2.3.5 lockable概念检查类:
2.3.6 lock函数:
除了使用mutex的成员函数或者lock_guard/unique_lock,还可以使用两个自由函数lock()和try_lock()来操作mutex,它们类似make_unique_locks(),可以一次锁定多个互斥量,而且保证不会出现死锁
2.3.7 thread:
thread类实现了操作系统里的线程表示,负责启动和管理线程对象,在概念和操作上都与POSIX线程相似。
四个静态函数:
get_id():同thread的同名函数,用来获得thread::id
yield():指示当前线程放弃时间片,允许其他的线程运行
sleep_for():线程睡眠等待一个小段时间
sleep_until():线程睡眠等待至一个时间点
2.3.8 启动线程:
线程启动可以用bind或者lambda表达式
thread t1(bind(dummy,100));
thread t2([]{dummy(500);});
sleep_for是在死等线程,因此有join等待线程。thread的成员函数joinable()可以判断thread对象是否标识了一个可执行的线程体。如果joinable()返回true,我们就可以调用成员函数join来阻塞等待线程执行结束
detach分离线程:临时对象启动线程,与线程执行体分离,但线程继续运行
thread_guard:用于控制thread对象析构的行为
scoped_thread:它可以使用模板参数定制析构的动作
2.3.9 中断线程:
interrupt():要求线程中断执行
interruption_requested():检查线程是否被要求中断
线程的12个中断点
启用/禁用线程中断
2.3.10 thread_group:
用于管理一组线程,就像是一个线程池
2.3.11 call_once:
为了保证在多线程环境中初始化函数可被正确调用,thread库提供了仅调用一次的机制,使用多个线程在操作函数时,只能又一个线程成功执行,避免多次执行导致错误。以once_flag对象作为初始化标志,再使用call_once来调用函数,完成仅执行一次的初始化
2.3.12 条件变量:conditon
条件变量是另一种用于等待的同步机制,可以实现线程间的通信,它必须与互斥量配合使用,等待另一个线程中某个事件的发生(满足某个条件),然后才可以继续执行。
2.3.13 shared_mutex:
它允许线程获取多个共享所有权和一个专享所有权,实现了读写锁的机制,即多个读线程一个写线程
2.3.14 future:
很多情况下线程不仅仅要执行一些工作,它还可能要返回一些计算结果。thread库使用future提供了异步操作线程返回值的方法,因为这个返回值在线程开始执行似乎还是不可用的,是一个“未来”的“期待值”
2.3.15 shared_future:
多次调用future