thread
thread库需要chrono库提供时间概念来执行睡眠、等该操作。因此必须先编译chrono库
一、
mutex(互斥量):可以在多线程环境里面防止多线程同时操作共享资源。
thread提供了6种互斥量:
null_mutex:无任何锁定功能的互斥量
mutex:独占式互斥量,最简单实用。
timed_mutex:独占式互斥量,提供超时锁定功能。
recursive_mutex:递归式互斥量,可以多次锁定,同时需要多次解锁。
recursive_timed_mutex:
shared_mutex:C++14,读写锁
这些互斥量除了功能不同外基本接口都比较接近。
mutex用法:
boost::mutex mu;
try
{
mu.lock();//用于线程阻塞等待直到获取互斥量的所有权
//操作共享资源
mu.unlock();
}
catch ()
{
mu.unlock();
}
注:使用try catch 以防异常发生,未调用unlocks
timed_mutex用法:
boost::timed_mutex mu;//定时互斥量
auto flag = mu.try_lock_for(100_ms);//等待100ms
if(flag)//检查是否成功解锁互斥量
{
cout << "lock mutex"<
二、lock_guard(RAII)
辅助锁定互斥量,构造时候锁定互斥量,解析时候自动解锁,不可拷贝
mutex mu;
lock_guard g(mu);//第一种构造形式
//执行操作
timed_mutex mu;
if(mu.try_lock_for(100ms))
{
lock_guard g(mu,adopt_lock);//第二种构造形式,认为线程之前已经锁定mutex,不会再执行锁定,但是会解锁。
//访问共享资源
}
unique_lock
注:工作机制和guard_lock相同,但是更加复杂,构造函数还可以接受其他的锁定选项,从而有不同的行为。
thread库中三个锁定选项:
struct defer_lock_t{};
struct try_to_lock_t{};
struct adopt_lock_t{};
在unique_lock构造函数里面使用这些标志常量就可以使得unique_lock产生不同的效果:
defer_lock: is_locked =false,不执行锁定功能
try_to_lock: is_locked=false,执行try_lock;
adopt_lock: is_locked=true,不执行锁定功能
工厂函数:
注:因为unique_lock支持转移语义,所以它可以从工厂函数产生。关于转移语义,工厂函数后面再谈。
mutex mu,mu1,mu2;
{
auto g = make_unique_lock(mu);//工厂函数锁定互斥量
assert(g.owns_lock());//断言已经锁定
//执行操作
}
{
auto g = make_unique_lock(mu, defer_lock);//暂时不锁定互斥量
assert(!g);
assert(g.try_lock());//尝试锁定
assert(g);
//执行操作
}
//thread没有提供时间限定形式的工厂函数,实现自己的重载形式
template
unique_lock my_make_lock(Lockable& mtx, D d)
{
return unique_lock(mtx, d);
}
timed_mutex tm;
auto g = make_unique_locks(mu, tm);
lock适配器:
lock_guard和unique_lock大多数情况下和mutex搭配使用,用于锁定互斥量。但是他们是模板类,所以只要符合lockable概念,也就是有lock/unlock/try_lock接口的类都可以用于lock_guard和unique_lock。
thread定义的lockable适配器:
basic_lockable_adapter;
lockable_adapter;
timed_lockable_adapter;
例如:
class account final:public lockable_adapter
{
}//该类继承于public lockable_adapter,自动获得lock接口,因此接下来可讲account类用于unique_lock或者lock_guard。
account a; //account实例,可锁定
auto g = make_unique_lock(a); //无需其他mutex,自身可锁定
或者:
auto = make_unique_lock(a,try_to_lock);
lock函数:
除了使用mutex成员函数、LOck_guard/unique_lock,还可以使用自由函数lock()和try_lock(),类似与make_unique_locks(),可以一次性锁定多个互斥量,并且保证不出现锁死。
但是其没有推出作用域自动解锁的特性,
但是保证发生异常就会解除对互斥量的锁定。
可以先使用make_unique_lock()的adopt_lock或者defer_lock锁定选项,但是暂时不锁定互斥量,然后用lock()或者try_lock()一次性锁定,退出时自动解锁。
mutex m1;
auto g1 = make_unique_lock(m1,adopt_lock);
lock(m1);
p:
1>------ 已启动生成: 项目: test_thread, 配置: Debug x64 ------
1> test_thread.cpp
1>D:\programing\boost_1_61_0\boost/type_traits/common_type.hpp(43): fatal error C1001: 编译器中发生内部错误。
1> (编译器文件“msc1.cpp”,第 1325 行)
1> 要解决此问题,请尝试简化或更改上面所列位置附近的程序。
1> 请选择 Visual C++
1> “帮助”菜单上的“技术支持”命令,或打开技术支持帮助文件来获得详细信息。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========
thread:
实现了操作系统里的线程表示,负责启动和管理线程对象。
thread对象不可拷贝不可比较,但支持move,有转移构造函数和转移赋值函数,所以允许从工厂函数产生。
thread t1,t2;
cout << t1.get_id()<
assert(t1.get_id()==t2.get_id());//比较两个线程对象,但不符合c++标准hardware_concurrency()可以获得系统可以并发的线程数量。
physical_concurrency可以获得物理cpu核心数量。
自由函数boost::this_thread::get_id()/yield()/sleep_for()/sleep_until() 无需使用thread对象就可以操作当前线程。
线程执行流程:
1.当创建thread对象后,线程就立即执行
void dummy(int n)
{
for(int i =0; i < n ; i ++)
cout << n<
2.等待线程结束
thread成员函数
joinable()
可以用一个来判断thread对象是否标注了一个可执行的线程体。
join等待线程:
join()
:一直等待直到线程结束
try_join_for()/try_join_until()
:阻塞线程一定时间段,最后不管线程是否结束都结束返回。如果线程在时间段内结束,则直接返回。
thread t1(bind(dummy,100));//使用bind表达式启动线程
thread t2([]{dummy(500);});//使用lambda启动线程
t1.try_join_for(100ms);
t2.join();
detach分离线程:
成员函数detach()将thread与线程执行体手动分离,此后thread对象不代表任何线程体,joinable==0,此时线程仍将不受影响地继续执行,直到函数结束,或者跟随主进程一起结束。
#include
using namespace std;
#include
#include
void dummy(int n)
{
for (int i = 0; i < n; i++)
cout << n << endl;
}
int main()
{
boost::thread t1(dummy, 500);
boost::thread t2(dummy, 1000);
cout << t1.joinable() << endl;
t1.detach();//与线程执行体分离
cout << t1.joinable() << endl;
boost::thread_guard<>g2(t2);//析构时等待线程结束
}
中断线程
interrupt(),interruption_requested();
注:线程并不是在任何时候都能中断,只有当线程执行到中断点的时候才能被中断。
线程末仍情况下都是允许被中断的,thread库允许进一步控制线程的中断行为。
interruption_enable()//检测当前函数是否允许中断
interruption——requested//检测当前线程是否被要求中断
thread_group
用于管理一组线程,内部使用std::list来容纳创建的thread对象
成员函数create_thread()是一个工厂函数,可以创建thread对象并且运行程序,同时加入内部list。但是不支持如thread构造函数那样传递函数参数的用法,所以用bind或者lambda来包装执行的函数。
void dummy(int x)
{
for (int i = 0; i < x; i++)
cout << x << endl;
}
int main()
{
thread_group tg;
tg.create_thread(bind(dummy, 200));
tg.join_all();//等待所有线程执行结束
}
call_once
在多线程调用函数时候只能有一个线程调用成功
int g_count;//全局变量,目标:一次初始化
void init_count(int x)
{
cout << "should call once." << endl;
g_count = x;
}
void call_func()
{
static once_flag two;//一次初始化标志,调用call_once()必须先申明once_flag对象,而不能用临时变量
call_once(two, init_count, 10);
}
int main()
{
thread_group tg;
tg.create_thread(bind(call_func));
tg.create_thread(bind(call_func));
tg.join_all();
}
条件变量
另一种用于等待的机制,可以实现进程之间通信,必须和互斥量配合使用,等待另一个线程中某个事情发生
thread库提供两种条件变量对象:
condition_Variable
condition_Variable_any//能够适应更广泛的互斥量类型。
shared_mutex
允许线程获取倒戈 共享所有权和一个专享所有权,即可以实现多个读线程一个写线程。
feature:
异步操作线程返回值的方法。
函数async()函数用于产生feature对象,异步启动一个线程运行函数,返回feature对象,随后就可以利用feature获取计算结果。
相比于thread其更关心函数的计算结果而不是过程。