线程
thread
intmain(){threadt1(Test1); t1.join();threadt2(Test2); t2.join(); thread t3 = t1;threadt4(t1); thread t5 =std::move(t1);threadt6(std::move(t1));return0;}
t3,t4创建失败,因为thread的拷贝构造和赋值运算符重载的原型是:
thread(constthread&) =delete;thread&operator=(constthread&) =delete;
被禁用了,但是t5, t6线程是创建成功的。std::move把t1转换为右值,调用的是函数原型为thread& operator=(thread&& _Other) noexcept和thread(thread&& _Other) noexcept。
当线程对象t1被移动拷贝和移动赋值给t5和t6的时候,t1就失去了线程控制权,也就是一个线程只能同时被一个线程对象所控制。最直观的是t1.joinable()返回值为false,joinable()函数后面介绍。
使用类成员函数作为线程参数:
classTask{public: Task(){}voidTask1(){}voidTask2(){}private:};intmain(){ Task task;threadt3(&Task::Task1, &task); t3.join();return0;}
关键点是要创建一个类对象,并作为第二个参数传入thread()线程的构造函数中去。
管理当前线程的函数
yield
此函数的准确性为依赖于实现,特别是使用中的 OS 调度器机制和系统状态。例如,先进先出实时调度器( Linux 的 SCHED_FIFO )将悬挂当前线程并将它放到准备运行的同优先级线程的队列尾(而若无其他线程在同优先级,则 yield 无效果)。
#include<iostream>#include<chrono>#include<thread>// 建议其他线程运行一小段时间的“忙睡眠”voidlittle_sleep(std::chrono::microseconds us){autostart =std::chrono::high_resolution_clock::now();autoend = start + us;do{std::this_thread::yield(); }while(std::chrono::high_resolution_clock::now() < end);}intmain(){autostart =std::chrono::high_resolution_clock::now(); little_sleep(std::chrono::microseconds(100));autoelapsed =std::chrono::high_resolution_clock::now() - start;std::cout<<"waited for "<(elapsed).count() <<" microseconds\n";}
get_id
这个函数不用过多介绍了,就是用来获取当前线程id的,用来标识线程的身份。
std::thread::id this_id =std::this_thread::get_id();
sleep_for
位于this_thread命名空间下,msvc下支持两种时间参数。
std::this_thread::sleep_for(2s);std::this_thread::sleep_for(std::chrono::seconds(1));
sleep_untile
参数构建起来挺麻烦的,一般场景下要求线程睡眠的就用sleep_for就行了
usingstd::chrono::system_clock;time_ttt = system_clock::to_time_t(system_clock::now());structstd::tm *ptm = localtime(&tt);std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
互斥
mutex
对于互斥量看到一个很好的比喻:
单位上有一台打印机(共享数据a),你要用打印机(线程1要操作数据a),同事老王也要用打印机(线程2也要操作数据a),但是打印机同一时间只能给一个人用,此时,规定不管是谁,在用打印机之前都要向领导申请许可证(lock),用完后再向领导归还许可证(unlock),许可证总共只有一个,没有许可证的人就等着在用打印机的同事用完后才能申请许可证(阻塞,线程1lock互斥量后其他线程就无法lock,只能等线程1unlock后,其他线程才能lock),那么,这个许可证就是互斥量。互斥量保证了使用打印机这一过程不被打断。
代码示例:
mutex mtx;intgNum =0;voidTest1(){ mtx.lock();for(intn =0; n <5; ++n) gNum++; mtx.unlock();}voidTest2(){std::cout<<"gNum = "<< gNum <
join()表示主线程等待子线程结束再继续执行,如果我们的期望是打印循环自增之后的gNum的值,那t1.join()就放在t2创建之前调用。因为t2的创建就标志着t2线程创建好然后开始执行了。
通常mutex不单独使用,因为lock和unlock必须配套使用,如果忘记unlock很可能造成死锁,即使unlock写了,但是如果在执行之前程序捕获到异常,也还是一样会死锁。如何解决使用mutex造成的死锁问题呢?下面介绍unique_gard和lock_guard的时候详细说明。
timed_mutex
提供互斥设施,实现有时限锁定
recursive_mutex
提供能被同一线程递归锁定的互斥设施
recursive_timed_mutex
提供能被同一线程递归锁定的互斥设施,并实现有时限锁定
通用互斥管理
lock_guard
voidTest1(){std::lock_guard lg(mtx);for(intn =0; n <5; ++n) { gNum++;std::cout<<"gNum = "<< gNum <
龙华大道1号 http://www.kinghill.cn/Dynamics/2106.html