C++学习笔记--多线程
2016-08-26 22:35
260 查看
线程与进程优缺点对比:
多线程开销小,但难于管理,且不能用于分布式系统;
多进程开销大,操作系统会进行一部分管理,因此用户管理就比较简单,可用于分布式;
通常多线程和多进程结合使用。
参考资料:http://edu.csdn.net/course/detail/2303/35894?auto_start=1
代码实例:
from t1: 0
from t1: -1
from t1: -2
from main: 0
from main: 1
from main: 2
from main: 3
from main: 4
from main: 5
from main: 6
from main: 7
from t1: -3
from t1: -4
from t1: -5
可以加mutex锁,有线程正在使用cout,其他线程就不能使用,这样cout在当前程序中是线程安全的,能使得输出是有序的,是下面这样的:
from main: 0
from t1: 0
from main: 1
from t1: -1
from main: 2
from t1: -2
from main: 3
from t1: -3
from main: 4
from t1: -4
from main: 5
from t1: -5
上面的代码中,当shared_print函数中cout那一行抛出异常时,mtx.unclock()不会被执行,mtx将被永远地锁住。这时可以使用std::lock_guard来保证mtx会被解锁。shared_print函数修改如下:
但是上面的代码仍然不是安全的,因为cout是个全局变量,并没有完全在mtx的保护下,其他线程仍然可以在不加锁的情况下使用cout。为了完整地保护资源,必须使资源和互斥对象进行绑定。代码如下:
上面的代码将资源std::ofstream f和互斥对象std::mutex m_mutex定义在LogFile类中,类外的线程不可访问资源f,使用类对象的shared_print函数的线程也能保证资源f必定有一个互斥对象m_mutex来保护。简单地讲,就是资源和互斥对象必定成对地出现在同一个作用域中,因此资源一定会受互斥对象保护。注意,这里的代码使用了资源std::ofstream f,而不是cout,是因为cout是全局的资源。
from t1: 0
from main: 0
from t1: -1
from main: 1
上面的代码中,m_mutex和m_mutex2在两个线程中加锁的顺序是相反的,如果将语句的顺序改成一致就不会发生死锁。在C++标准库中提供了std::lock,是规范的处理死锁问题的方法,把上面的两个函数改成下面这样:
上面的代码中,即使locker和locker2的顺序是相反的,但是m_mutex和m_mutex2加锁的顺序是相同的,因为std::lock指定了加锁的顺序。
为了避免程序设计中出现死锁,可以遵循以下几条规则:
(1)使用一个mutex即可满足要求的场合,绝不使用两个mutex;
(2)如果某个作用域中已经使用了一个mutex,那么要小心该作用域中的函数调用,因为该函数调用中可能包括其他mutex;
(3)无法避免地需要使用两个以上mutex时,尽量使用std::lock指定锁的顺序,但是在某些极端情况下std::lock无法使用,就要小心地保证加锁的语句顺序。
上面使用的所有示例代码中,LogFile类都是在构建函数中打开log.txt文件,如果我们想只在调用shared_print函数的时候才打开文件,可以做如下修改:
std::promise可以从父线程获取值到子线程中使用
如果需要创建多个都调用factorial线程,每个线程都需要一个f参数,但是std::future不能被拷贝,此时可以使用std::shared_future,它可以被拷贝,可将int factorial(std::future& f)改为int factorial(std::shared_future f),同时main函数做如下修改:
多线程开销小,但难于管理,且不能用于分布式系统;
多进程开销大,操作系统会进行一部分管理,因此用户管理就比较简单,可用于分布式;
通常多线程和多进程结合使用。
参考资料:http://edu.csdn.net/course/detail/2303/35894?auto_start=1
代码实例:
1 最简单的多线程
#include <iostream> #include <thread> void function_1() { std::cout <<"www.oxox.work"<<std::endl; } int main() //主线程 { std::thread t1(function_1); //创建并初始化一个线程,且线程t1创建完之后就开始运行 //t1.detach(); //主线程和t1线程互不影响,detach可以使主线程不等待t1线程结束即可运行,这样会使得程序的运行结果没有内容输出,因为主线程还没有等到t1输出内容就结束了。线程被deatch之后就不能再join了,如果再join,编译不会报错,但是运行会报错 if(t1.joinable()) //如果执行了t1.detach(),t1.joinable()为false { t1.join(); //主线程将等待t1线程结束后再运行 } return 0; }
2 主线程和子线程交叉运行
#include <iostream> #include <thread> using namespace std; void function_1() { cout <<"www.oxox.work"<<endl; } class Factor { public: void operator()() { for(int i = 0; i > -100; --i) { cout << "from t1: " << i << endl; } } }; int main() //主线程 { //std::thread t1(function_1); //使用函数创建并初始化一个线程,且线程开始运行 Factor fct; std::thread t1(fct); //使用函数对象创建并初始化一个线程 try { for(int i = 0; i < 100; ++i) { cout << "from main: " << i << endl; } } catch(...) //上面的for循环属于主线程,如果上面抛出异常,但是没有try catch,主线程终止,t1线程也终止了,这样是非线程安全的。添加try catch之后,即使主线程异常,t1线程也能正常执行结束 { t1.join(); //主线程将等待t1线程结束后再运行 throw; } return 0; }
3 主线程和子线程之间实现内存共享
#include <iostream> #include <thread> #include <string> using namespace std; void function_1() { cout <<"www.oxox.work"<<endl; } class Factor { public: void operator()(string &s) { cout << "from t1: " << s << endl; s = "I love XuHuanDaXue"; } }; int main() //主线程 { string s("I love www.oxox.work"); //string变量s被主线程和t1线程使用,可通过s实现内存共享 Factor fct; //std::thread t1(fct, s); //这种方式并不能在t1线程中改变s,因为s将被拷贝 std::thread t1(fct, std::ref(s)); //t1线程可改变s,因为参数是s的引用 t1.join(); cout << "from main: " << s.c_str() << endl; //主线程使用了被t1线程改变的s return 0; }
4 线程移动与线程ID
#include <iostream> #include <thread> #include <string> using namespace std; void function_1() { cout <<"www.oxox.work"<<endl; } class Factor { public: void operator()(string &s) { cout << "from t1: " << s << endl; s = "I love XuHuanDaXue"; } }; int main() //主线程 { string s("I love www.oxox.work"); Factor fct; cout << std::this_thread::get_id() << endl; //获取主线程ID,每个线程都有个ID std::thread t1(fct, std::move(s)); //此处s被移动,移动操作比拷贝要高效,比引用要安全 std::thread t2=std::move(t1); //线程对象只能被移动,但不能被拷贝,所以必须使用std::move() cout << t2.get_id() << endl; //获取t2线程ID //t1.join(); t2.join(); //t1被移动到t2,t1已经是空的了,所以得使用t2.join() cout << "from main: " << s.c_str() << endl; cout << std::thread::hardware_concurrency() <<endl; //检测CPU能支持的最大线程数,如果用户创建的线程数超过了CPU能支持的,反而会引起性能下降 return 0; }
5 线程安全
下面的代码是非线程安全的,主线程和t1线程将竞争资源cout,只要竞争到资源就随时可以将内容写入到输出流cout,使得输出看起来是下面这样的:from t1: 0
from t1: -1
from t1: -2
from main: 0
from main: 1
from main: 2
from main: 3
from main: 4
from main: 5
from main: 6
from main: 7
from t1: -3
from t1: -4
from t1: -5
#include <iostream> #include <thread> #include <string> using namespace std; void function_1() { for(int i = 0; i > -100; --i) { cout << "from t1: " << i << endl; } } int main() //主线程 { std::thread t1(function_1); for(int i = 0; i < 100; ++i) { cout << "from main: " << i << endl; } t1.join(); //主线程将等待t1线程结束后再运行 return 0; }
可以加mutex锁,有线程正在使用cout,其他线程就不能使用,这样cout在当前程序中是线程安全的,能使得输出是有序的,是下面这样的:
from main: 0
from t1: 0
from main: 1
from t1: -1
from main: 2
from t1: -2
from main: 3
from t1: -3
from main: 4
from t1: -4
from main: 5
from t1: -5
#include <iostream> #include <thread> #include <string> #include <mutex> using namespace std; std::mutex mtx; void shared_print(string s, int id) { mtx.lock(); cout << s.c_str() << id << endl; mtx.unlock(); } void function_1() { for(int i = 0; i > -100; --i) { shared_print("from t1: ",i); } } int main() //主线程 { std::thread t1(function_1); for(int i = 0; i < 100; ++i) { shared_print("from main: ",i); } t1.join(); //主线程将等待t1线程结束后再运行 return 0; }
上面的代码中,当shared_print函数中cout那一行抛出异常时,mtx.unclock()不会被执行,mtx将被永远地锁住。这时可以使用std::lock_guard来保证mtx会被解锁。shared_print函数修改如下:
void shared_print(string s, int id) { std::lock_guard<std::mutex> locker(mtx); //lock_guard对象创建时会自动对mtx加锁,离开作用域被析构时,mtx会被自动解锁,这样即使cout这行发生异常,mtx也能被解锁了 cout << s.c_str() << id << endl; }
但是上面的代码仍然不是安全的,因为cout是个全局变量,并没有完全在mtx的保护下,其他线程仍然可以在不加锁的情况下使用cout。为了完整地保护资源,必须使资源和互斥对象进行绑定。代码如下:
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> using namespace std; class LofFile { public: LofFile() { f.open("log.txt"); } void shared_print(string s, int id) { std::lock_guard<std::mutex> locker(m_mutex); f << s << id << endl; } private: std::mutex m_mutex; std::ofstream f; }; void function_1(LofFile& log) { for(int i = 0; i > -100; --i) { log.shared_print("from t1: ",i); } } int main() //主线程 { LofFile log; std::thread t1(function_1, std::ref(log)); for(int i = 0; i < 100; ++i) { log.shared_print("from main: ",i); } t1.join(); //主线程将等待t1线程结束后再运行 return 0; }
上面的代码将资源std::ofstream f和互斥对象std::mutex m_mutex定义在LogFile类中,类外的线程不可访问资源f,使用类对象的shared_print函数的线程也能保证资源f必定有一个互斥对象m_mutex来保护。简单地讲,就是资源和互斥对象必定成对地出现在同一个作用域中,因此资源一定会受互斥对象保护。注意,这里的代码使用了资源std::ofstream f,而不是cout,是因为cout是全局的资源。
6 避免死锁
死锁是指两个线程互相锁住,互相等待释放,却不能释放,下面的代码发生了死锁,程序的输出可能是这样的(程序被暂停在某处,并没有成功执行完毕):from t1: 0
from main: 0
from t1: -1
from main: 1
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> using namespace std; class LofFile { public: LofFile() { f.open("log.txt"); } void shared_print(string s, int id) //函数被t1线程调用 { std::lock_guard<std::mutex> locker(m_mutex); std::lock_guard<std::mutex> locker2(m_mutex2); cout << s << id << endl; //为了更直观得看到结果,这里改成cout } void shared_print2(string s, int id) //函数被主线程调用 { std::lock_guard<std::mutex> locker2(m_mutex2); std::lock_guard<std::mutex> locker(m_mutex); cout << s << id << endl; //为了更直观得看到结果,这里改成cout } //如果t1线程执行到锁住m_mutex时,主线程正好执行到锁住m_mutex2,t1线程继续执行下一语句发现m_mutex2被锁住了,于是等待m_mutex2被解锁,而主线程也继续执行下一语句发现m_mutex被锁住了,于是等待m_mutex被解锁,两个线程相互等待,这样就发生了死锁,程序就一直暂停在哪里 private: std::mutex m_mutex; std::mutex m_mutex2; std::ofstream f; }; void function_1(LofFile& log) { for(int i = 0; i > -100; --i) { log.shared_print("from t1: ",i); } } int main() //主线程 { LofFile log; std::thread t1(function_1, std::ref(log)); for(int i = 0; i < 100; ++i) { log.shared_print2("from main: ",i); } t1.join(); //主线程将等待t1线程结束后再运行 return 0; }
上面的代码中,m_mutex和m_mutex2在两个线程中加锁的顺序是相反的,如果将语句的顺序改成一致就不会发生死锁。在C++标准库中提供了std::lock,是规范的处理死锁问题的方法,把上面的两个函数改成下面这样:
void shared_print(string s, int id) //函数被t1线程调用 { std::lock(m_mutex, m_mutex2); //std::lock可以指定锁的顺序,参数为lock1,lock2,...,lockn,它的参数个数是不固定的,有多少个锁就可以使用多少个参数 std::lock_guard<std::mutex> locker(m_mutex, std::adopt_lock); //这里添加std::adopt_lock是告知locker,m_mutex已经被锁住,locker要做的只是获得m_mutex的所有权,然后在析构时将其解锁即可 std::lock_guard<std::mutex> locker2(m_mutex2, std::adopt_lock); cout << s << id << endl; //为了更直观得看到结果,这里改成cout } void shared_print2(string s, int id) //函数被主线程调用 { std::lock(m_mutex, m_mutex2); std::lock_guard<std::mutex> locker2(m_mutex2, std::adopt_lock); std::lock_guard<std::mutex> locker(m_mutex, std::adopt_lock); cout << s << id << endl; //为了更直观得看到结果,这里改成cout }
上面的代码中,即使locker和locker2的顺序是相反的,但是m_mutex和m_mutex2加锁的顺序是相同的,因为std::lock指定了加锁的顺序。
为了避免程序设计中出现死锁,可以遵循以下几条规则:
(1)使用一个mutex即可满足要求的场合,绝不使用两个mutex;
(2)如果某个作用域中已经使用了一个mutex,那么要小心该作用域中的函数调用,因为该函数调用中可能包括其他mutex;
(3)无法避免地需要使用两个以上mutex时,尽量使用std::lock指定锁的顺序,但是在某些极端情况下std::lock无法使用,就要小心地保证加锁的语句顺序。
7 Unique Lock和call_once
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> using namespace std; class LogFile { public: LogFile() { f.open("log.txt"); } void shared_print(string s, int id) { //std::lock_guard<std::mutex> locker(m_mutex); //lock_guard对象在构造时自动m_mutex加锁,在析构时自动对m_mutex解锁,用户无法自由控制何时加锁解锁 //std::unique_lock<std::mutex> locker(m_mutex); //unique_lock可以起到与lock_guard一样的功能,默认情况下是构造自动加锁,析构自动解锁 std::unique_lock<std::mutex> locker(m_mutex, std::defer_lock); //还可以使用defer_lock告知locker,不要在构造时自动加锁m_mutex locker.lock(); //然后用户自行给m_mutex加锁 f << s << id << endl; locker.unlock(); //还可以在需要的时候自行解锁,这样m_mutex只锁住了上面一行语句,之后的操作就没有被m_mutex锁住 //代码块... locker.lock(); //接下来还可以随意地自行加锁和解锁 //代码块... locker.unlock(); //lock_guard和unique_lock都不能被复制,但是unique_lock可被移动。当unique_lock被移动时,m_mutex的控制权也从一个unique_lock转移到另一个unique_lock。另外,unique_lock的代价比lock_guard高,所以使用lock_guard即可满足要求的场合就使用lock_guard //std::unique_lock<std::mutex> locker2 = std::move(locker); } private: std::mutex m_mutex; std::ofstream f; }; void function_1(LogFile& log) { for(int i = 0; i > -100; --i) { log.shared_print("from t1: ",i); } } int main() //主线程 { LogFile log; std::thread t1(function_1, std::ref(log)); for(int i = 0; i < 100; ++i) { log.shared_print("from main: ",i); } t1.join(); //主线程将等待t1线程结束后再运行 return 0; }
上面使用的所有示例代码中,LogFile类都是在构建函数中打开log.txt文件,如果我们想只在调用shared_print函数的时候才打开文件,可以做如下修改:
class LogFile { public: LogFile() { //f.open("log.txt"); } void shared_print(string s, int id) { //if(!f.is_open()) //这段代码不是线程安全的,因为两个线程可能同时执行到f.open处,两次打开文件,将会运行报错 //{ // f.open("log.txt"); //} //if(!f.is_open()) //这段代码仍然不是线程安全的,假设a线程尚未执行完f.open,b线程正好检测到文件未打开,然后发现m_mutex_fopen被锁住,于是等待,接着a线程打开了文件,解锁m_mutex_fopen,此时b线程发现解锁了,立即执行f.open,这样就两次打开文件,将会运行报错 //{ // std::unique_lock<std::mutex> locker(m_mutex_fopen, std::defer_lock); // f.open("log.txt"); //} //{ //这段代码是线程安全的,但是这存在一个性能上的问题,即每次函数调用都要对m_mutex_fopen进行加锁和解锁,这会无意义地消耗计算机资源 // std::unique_lock<std::mutex> locker(m_mutex_fopen, std::defer_lock); // if(!f.is_open()) f.open("log.txt"); //} std::call_once(m_flag, [&](){f.open("log.txt")}) //这行代码能确保后面的lambda函数只被一个线程调用一次,是C++标准库的推荐用法 std::unique_lock<std::mutex> locker(m_mutex, std::defer_lock); f << s << id << endl; } private: std::mutex m_mutex; std::mutex m_mutex_fopen; std::once_flag m_flag; std::ofstream f; };
8 条件变量
条件变量适用于a线程需要等待b线程触发某种条件,a线程才能执行的场合#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> #include <deque> #include <functional> #include <condition_variable> using namespace std; std::deque<int> q; std::mutex mu; std::condition_variable cond; void function_1() { int count = 10; while (count > 0) { std::unique_lock<mutex> locker(mu); q.push_front(count); locker.unlock(); cond.notify_one(); //激活条件变量cond cond.notify_all(); //notify_one只能激活一个正在等待cond被激活的线程 //notify_all可以激活所有正在等待cond被激活的线程 std::this_thread::sleep_for(chrono::seconds(1)); count--; } } void function_2() { int data = 0; while (data != 1) { std::unique_lock<mutex> locker(mu); /*if (!q.empty()) { data = q.back(); q.pop_back(); locker.unlock(); cout << "t2 got a value from t1: " << data << endl; } else //当q为空时,一直在执行while循环,不断查询,直到q非空,这样是很低效的,应该使用条件变量 { locker.unlock(); }*/ //等待线程1调用notify_one()激活条件变量bond,只有cond被激活,才可以 //执行后面的语句。此处locker作为参数传递给wait之前已经加锁了,一个 //线程不会在锁住的情况下休眠,所以wait()先将mu解锁,使其休眠,然后 //又加锁。由于需要重复加解锁,所以此处得用unique_lock,而不能使用 //lock_guard //某些情况下,线程可能被自己激活,这称为伪激活,给条件变量添加一个 //lambda函数作为增加的一个条件,满足条件才可以被激活 cond.wait(locker, [](){return !q.empty(); }); data = q.back(); q.pop_back(); locker.unlock(); cout << "t2 got a value from t1: " << data << endl; } } int main() { std::thread t1(function_1); std::thread t2(function_2); t1.join(); t2.join(); return 0; }
9 Future, Promise和async()
std::future类可以从子线程获取返回值,然后在父线程中使用#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> #include <future> using namespace std; int factorial(int N) { int res = 1; for (int i = N; i > 1; --i) { res *= i; } cout << "Result is: "<< res << endl; return res; } int main() { int x; //std::thread t1(factorial,4); //t1.join(); //有时希望将子线程的执行结果返回给父线程,可以修改上面的代码将x的引用 //传给t1,通过内存共享的方式来实现,但是更简单的方式是使用future对象, //取名future的含义是,这个对象可以从未来获取某个值,即等待未来子线程执 //行结束时返回的值。实际上async并不一定会创建子线程,如果明确指定第一 //个参数为std::launch::deferred,将不创建子线程,此时factorial()调用将被延期, //等到get()执行后,就在父线程中调用factorial。如果参数为std::launch::async, //就是明确指定创建子线程。async的参数默认是std::launch::async | std::launch::deferred, //意思是是否创建子线程将取决于实现(没有弄明白取决于什么实现?) //std::future<int> fu = std::async(factorial, 4); std::future<int> fu = std::async(std::launch::async | std::launch::deferred, factorial, 4); x = fu.get(); //get将会等待子线程结束,并取回子线程返回的结果,get只能被调用一次,调用两次程序会运行报错 return 0; }
std::promise可以从父线程获取值到子线程中使用
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> #include <future> using namespace std; int factorial(std::future<int>& f) { int N = f.get(); int res = 1; for (int i = N; i > 1; --i) { res *= i; } cout << "Result is: "<< res << endl; return res; } int main() { int x; std::promise<int> p; std::future<int> f = p.get_future(); std::future<int> fu = std::async(std::launch::async, factorial, std::ref(f)); p.set_value(4); //这里必须进行set_value,否则在factorial函数的int N = f.get()这行会抛出std::future_error::broken_promise的异常 x = fu.get(); cout << "Get from child: " << x << endl; return 0; }
如果需要创建多个都调用factorial线程,每个线程都需要一个f参数,但是std::future不能被拷贝,此时可以使用std::shared_future,它可以被拷贝,可将int factorial(std::future& f)改为int factorial(std::shared_future f),同时main函数做如下修改:
int main() { int x; std::promise<int> p; std::future<int> f = p.get_future(); std::shared_future<int> sf = f.share(); std::future<int> fu = std::async(std::launch::async, factorial, sf); std::future<int> fu2 = std::async(std::launch::async, factorial, sf); std::future<int> fu3 = std::async(std::launch::async, factorial, sf); p.set_value(4); return 0; }
10 创建线程的不同方式
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> #include <future> using namespace std; class A { public: void f(int x, char c){} int operator()(int N){return 0;} }; void foo(int x){} int main() { A a; std::thread t1(a, 6); //传递a的拷贝给子线程 std::thread t2(std::ref(a), 6); //传递a的引用给子线程 std::thread t3(std::move(a), 6); //从主线程移动a到子线程,a在主线程中不再有效 std::thread t4(A(), 6); //传递临时创建的a对象给子线程 std::thread t5(foo, 6); //传递自定义的函数给子线程 std::thread t6([](int x){return x*x;}, 6); //传递lambda函数给子线程 std::thread t7(&A::f, a, 8, 'w'); //传递a的拷贝的成员函数f给子线程 std::thread t8(&A::f, &a, 8, 'w'); //传递a的地址的成员函数f给子线程 std::async(std::launch::async, a, 6); //上面的八种方式同样适用于async return 0; }
11 Packaged_task
#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> #include <future> #include <deque> using namespace std; int factorial(int N) { int res = 1; for (int i = N; i > 1; --i) { res *= i; } cout << "Result is: "<< res << endl; return res; } std::deque<std::packaged_task<int()> > task_q; std::mutex mu; std::condition_variable cond; void thread_1() { std::packged_task<int()> t; { std::unique_lock<std::mutex> locker(mu); cond.wait(locker, []{return !task_q.empty();}); t = std::move(task_q.front()); } t(); } int main() { std::thread t1(thread_1); std::packaged_task<int()> t(std::bind(factorial, 6)); //packaged_task只能传递一个参数,如果还要传递参数6,可以使用bind函数 std::future<int> ret = t.get_future(); //获得与packaged_task共享状态相关联的future对象 { std::unique_lock<std::mutex> locker(mu); task_q.push_back(std::move(t)); } cond.notify_one(); int value = ret.get(); //等待任务完成并获取结果 t1.join() //这两句可以实现和packaged_task类似的功能,但是packaged_task特点是可以将一个可调用对象关联到一个future变量,然后异步获取可调用对象的返回结果 //auto t = std::bind(factorial, 6); //t(); //std::future<int> fu = std::async(factorial, 4); x = fu.get(); std::packaged_task<int(int)> t(factorial); //以一个可调用对象为参数,并且可以异步获取该调用对象的返回结果 std::future<int> ret = t.get_future(); //获得与packaged_task共享状态相关联的future对象 int value = ret.get(); //等待任务完成并获取结果 }
12 时间约束
可以让某个类对象休眠一段时间,或者到达指定的时间点才停止休眠#include <iostream> #include <thread> #include <string> #include <mutex> #include <fstream> #include <future> #include <deque> using namespace std; int factorial(int N) { int res = 1; for (int i = N; i > 1; --i) { res *= i; } cout << "Result is: " << res << endl; return res; } int main() { std::thread t1(factorial, 6); std::this_thread::sleep_for(chrono::milliseconds(3)); //线程休眠3毫秒 chrono::steady_clock::time_point tp = chrono::steady_clock::now() + chrono::milliseconds(3); //一个静态的时间点 std::this_thread::sleep_until(tp); //一直休眠,直到达到指定的时间点tp,才结束休眠 std::mutex mu; std::unique_lock<std::mutex> locker(mu); locker.try_lock_for(chrono::milliseconds(3)); locker.try_lock_until(tp); std::condition_variable cond; cond.wait_for(locker, chrono::milliseconds(3)); cond.wait_until(locker, tp); std::promise<int> p; std::future<int> f = p.get_future(); f.wait_for(chrono::milliseconds(3)); f.wait_until(tp); }
相关文章推荐
- C\C++ 程序员从零开始学习Android - 个人学习笔记(十) - java基础 - 多线程(待续)
- C++ 多线程 学习笔记(二)
- 【C++学习笔记】简单的多线程程序(摘自孙鑫C++教学视频)
- C++学习笔记(10)-信号处理&多线程
- more effective c++学习笔记 ---- 条款31
- 一个C++程序员的Delphi学习笔记
- unix_c++学习笔记1
- c++学习笔记
- c++学习笔记 2005-9
- C++ 学习笔记 I/O流的常用控制符
- 高质量C++/C编程指南学习笔记(上)
- c++入门学习笔记指针篇
- C++学习笔记-类的继承(派生类)
- C++学习笔记(1)——基类、派生类的对象空间
- C++学习笔记
- C++学习笔记(5)
- C++ :学习Proxy Class之笔记2
- C++的学习笔记
- c++学习笔记
- C++ 学习笔记