赞
踩
- 之前在Linux下操作C的API pthread相关函数 写多线程,或者使用Qt的QThread实现多线程编程,不太具备通用性
- C++11相较于C++98添加了蛮多的多线程编程的特性,本文记录下其中部分知识。
C++ 多线程编程入门基础: 学习链接
C++并发编程 - 同步并发操作:添加链接描述
代码解法类似 1.基础知识 里面的最后一个案例
class Foo { public: Foo() { } void first(function<void()> printFirst) { // printFirst() outputs "first". Do not change or remove this line. printFirst(); flag1 = true; cv.notify_all(); } void second(function<void()> printSecond) { unique_lock<mutex> loc(m); while(flag1==false){ cv.wait(loc); } // printSecond() outputs "second". Do not change or remove this line. printSecond(); flag2 = true; cv.notify_all(); } void third(function<void()> printThird) { unique_lock<mutex> loc(m); while(flag2==false){ cv.wait(loc); } // printThird() outputs "third". Do not change or remove this line. printThird(); } private: mutex m; condition_variable cv; bool flag1 = false; bool flag2 = false; };
condiation_variable的wait方法有两种:有条件的等待和无条件的等待
wait有条件等待与无条件等待
class FooBar { private: int n; mutex mtx; condition_variable cv; bool foo_done=false; public: FooBar(int n) { this->n = n; } void foo(function<void()> printFoo) { for (int i = 0; i < n; i++) { unique_lock<mutex> locker(mtx); while(foo_done==true) cv.wait(locker); // cv.wait(locker,[&](){return foo_done==false;}) printFoo(); foo_done=true; cv.notify_one(); } } void bar(function<void()> printBar) { for (int i = 0; i < n; i++) { unique_lock<mutex>locker(mtx); while(foo_done==false) cv.wait(locker); //cv.wait(locker,[&](){return foo_done==true;}) 除了while还可以采用条件wait printBar(); foo_done=false; cv.notify_one(); } }
/************************* example1 - join : main是主线程,t1是主线程中的子线程,在这个线程里执行函数fun1()。 使用join()函数,那么主线程就会一直阻塞在t1.join();的位置,直到子线程t1运行结束后,再接着执行后面的语句。 fun1()函数里写了一个休眠1s的语句,所以t1线程在还没有打印fun1的时候,主线程由于没有写join()函数阻塞起来,就会继续往后执行,先打印出main,等t1休眠了1s后,才打印出fun1。 join()函数除了阻塞作用,另一个作用就是回收该线程使用到的资源,上面的程序把join()删掉了,子线程t1的资源不能正确回收,程序运行会报错“abort() has been called”。 **************************/ #if 0 #include<iostream> #include <thread> #include <chrono> using namespace std; void ff() { cout << "func1" << endl; this_thread::sleep_for(chrono::seconds(1)); } int main() { thread t1(ff); //t1.join(); cout << "main" << endl; return 0; } #endif /************************* example2 - detach : 那如果子线程t1的运行时间太长了,我不想让主线程阻塞等待t1运行,怎么办呢?下面就介绍detach()函数。 detach()函数的作用是将子线程和主线程分离,让子线程在后台独立运行。如果主线程运行很快,先结束运行了,子线程还未执行完的话,那么子线程会在后台继续执行,执行完毕时由线程库回收其资源。 请看如下例子,主线程中创建子线程t1,通过detach()函数,t1从主线程中分离并开始运行,输出fun1()开始 ,然后休眠1s,这个过程中主线程输出main(),然后子线程休眠结束,输出fun1()结束。 **************************/ #if 0 #include<iostream> #include <thread> #include <chrono> using namespace std; void fun1() { cout << "fun1()开始" << endl; this_thread::sleep_for(chrono::seconds(1)); // 休眠1秒 cout << "fun1()结束" << endl; } int main() { thread t1(fun1); // 创建一个线程,名为t1,这个线程会执行fun1()函数 t1.detach(); this_thread::sleep_for(chrono::milliseconds(500)); // 休眠0.5秒 cout << "main()" << endl; system("pause"); } #endif /************************* example3 - 互斥锁mutex : - lock_guard 没有lock和unlock接口,靠析构函数自动释放,作用域是局部空间 粒度较大 - unique_lock 有lock和unlock的接口,可以锁定代码段,粒度更小 **************************/ #if 0 #include<iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; mutex m; void fun1() { lock_guard<mutex> loc(m); // 构造函数里自动加锁 cout << "fun1" << endl; // 做别的事情 // lock_guard出了这个作用域,自动解锁 } void fun2() { unique_lock<mutex> loc(m); // 构造函数里自动加锁 cout << "do A" << endl; loc.unlock(); // 解锁 loc.lock(); // 重新加锁 cout << "do B" << endl; loc.unlock(); // 解锁 } int main() { thread t1(fun2); // 创建一个线程,名为t1,这个线程会执行fun1()函数 t1.detach(); this_thread::sleep_for(chrono::milliseconds(500)); // 休眠0.5秒 cout << "main()" << endl; system("pause"); } #endif /************************* example4 - 条件变量condition_variable : 上述的unique_lock通常是和condition_variable配合使用的。当 condition_variable 对象的 wait() 函数被调用的时候,它使用 unique_lock来锁住当前线程。 当前线程会一直被阻塞,直到另外一个线程在相同的 condition_variable 对象上调用了notification()函数来唤醒当前线程。 **************************/ #if 0 /* //原始版本 线程顺序容易出错 #include<iostream> #include <thread> #include <mutex> #include <chrono> using namespace std; mutex m; class Foo { public: Foo() {} void first() { for (int i = 0; i < 10; ++i) { cout << "first" << endl; // 打印10个first } } void second() { for (int i = 0; i < 10; ++i) { cout << "second" << endl; // 打印10个second } } void third() { for (int i = 0; i < 10; ++i) { cout << "third" << endl; // 打印10个third } } }; int main() { Foo F; // 创建3个线程,分别调用first()、second()、third()函数打印 thread(&Foo::first, &F).detach(); thread(&Foo::second, &F).detach(); thread(&Foo::third, &F).detach(); system("pause"); } */ // 如何让三个线程按序打印,也就是不管哪个线程先被创建,都按照first()、second()、third()的顺序打印呢? /* 这时候就可以用前面提到过的unique_lock和condition_variable。我们设置两个标志位,flag1为true表示允许second打印、flag2为true表示允许third打印,先初始化flag1、flag2为false。线程创建后,可能出现以下的执行顺序: (1)如果在first()还未打印完时(或者压根还没开始执行时),执行second(),由于此时flag1被初始化为false,进入while循环,调用条件变量的wait函数,会将当前线程阻塞起来。此时需要等另一个子线程执行first()打印完毕后,将flag1设为true,然后调用notify_all()将阻塞的线程唤醒,second()才能接着执行,完成打印。 (2)如果first()已经执行完毕,然后执行second(),那么flag1为true,执行second()的那个线程不会进入while循环,也不会被阻塞起来,能顺利执行。 third()的执行先后也可参考以上两种情况。 */ #include<iostream> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> using namespace std; class Foo { private: mutex m; // 互斥锁 condition_variable cd_v; // 条件变量 bool flag1; // 两个标志位 bool flag2; public: Foo() : flag1(false), flag2(false) {} // 初始化两个标志位为false,表示second和third还不能打印 void first() { for (int i = 0; i < 10; ++i) { cout << "first" << endl; } flag1 = true; // first执行后,将flag1置为true,表示second可以打印了 cd_v.notify_all(); // 唤醒等待队列里的所有线程 } void second() { unique_lock<mutex> loc(m); // 加锁 // (1)先判断flag1是否为true,true则不会进入while循环,顺利向下执行; // (2)flag1为false则执行wait函数,释放锁,并进入对象cd_v的等待队列; // (3)当别的线程调用对象cd_v的notify_all()函数,会唤醒在等待队列中的这个线程,然后重新获得锁,继续步骤(1) while (flag1 == false) { cd_v.wait(loc); } for (int i = 0; i < 10; ++i) { cout << "second" << endl; } flag2 = true; // 修改flag2表示third已经可以打印了 cd_v.notify_all(); // 唤醒等待队列里的所有线程 // 当离开这个局部作用域时,unique_lock会调用析构函数,自动解锁 } void third() { unique_lock<mutex> loc(m); while (flag2 == false) { cd_v.wait(loc); } for (int i = 0; i < 10; ++i) { cout << "third" << endl; } } }; int main() { Foo F; // thread 需要传参,尤其对类对象,函数 + 类对象的地址 thread t1(&Foo::first, &F); thread t2(&Foo::second, &F); thread t3(&Foo::third, &F); t1.detach(); t2.detach(); t3.detach(); system("pause"); } #endif // 1 #if 0 // 题目一、交替打印字符串 #include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> #include <functional> using namespace std; class FooBar { private: int n; mutex mtx; condition_variable cv; bool foo_done = false; public: FooBar(int n) { this->n = n; } void foo(function<void()> printFoo) { for (int i = 0; i < n; i++) { unique_lock<mutex> locker(mtx); while (foo_done == true) cv.wait(locker); printFoo(); foo_done = true; cv.notify_one(); } } void bar(function<void()> printBar) { for (int i = 0; i < n; i++) { unique_lock<mutex>locker(mtx); while (foo_done == false) cv.wait(locker); printBar(); foo_done = false; cv.notify_one(); } } }; void printBar() { cout << "bar" << endl; } void printFoo() { cout << "Foo" << endl; } int main() { FooBar F(4); // thread 需要传参,尤其对类对象,函数 + 类对象的地址 thread t1(&FooBar::foo, &F,printBar); thread t2(&FooBar::bar,&F,printFoo); t1.detach(); t2.detach(); system("pause"); } #endif // 1 #if 1 // 题目一、交替打印字符串 #include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> #include <functional> using namespace std; int n = 5; mutex mtx; condition_variable cv; bool foo_done = false; void foo(function<void(int)> printFoo) { for (int i = 0; i < n; i++) { unique_lock<mutex> locker(mtx); while (foo_done == true) cv.wait(locker); printFoo(i); foo_done = true; cv.notify_one(); } } void bar(function<void(int)> printBar) { for (int i = 0; i < n; i++) { unique_lock<mutex>locker(mtx); while (foo_done == false) cv.wait(locker); printBar(i); foo_done = false; cv.notify_one(); } } void printBar(int i) { cout << "bar" <<i<< endl; } void printFoo(int i) { cout << "Foo" <<i<< endl; } int main() { // thread 需要传参,尤其对类对象,函数 + 类对象的地址 thread t1(foo, printBar); thread t2(bar,printFoo); t1.detach(); t2.detach(); system("pause"); } #endif // 1
// 题目2 多线程顺序执行 #if 0 #include<iostream> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> using namespace std; class Foo { private: mutex m; // 互斥锁 condition_variable cd_v; // 条件变量 bool flag1; // 两个标志位 bool flag2; public: Foo() : flag1(false), flag2(false) {} // 初始化两个标志位为false,表示second和third还不能打印 void first() { for (int i = 0; i < 10; ++i) { cout << "first" << endl; } flag1 = true; // first执行后,将flag1置为true,表示second可以打印了 cd_v.notify_all(); // 唤醒等待队列里的所有线程 } void second() { unique_lock<mutex> loc(m); // 加锁 // (1)先判断flag1是否为true,true则不会进入while循环,顺利向下执行; // (2)flag1为false则执行wait函数,释放锁,并进入对象cd_v的等待队列; // (3)当别的线程调用对象cd_v的notify_all()函数,会唤醒在等待队列中的这个线程,然后重新获得锁,继续步骤(1) while (flag1 == false) { cd_v.wait(loc); } for (int i = 0; i < 10; ++i) { cout << "second" << endl; } flag2 = true; // 修改flag2表示third已经可以打印了 cd_v.notify_all(); // 唤醒等待队列里的所有线程 // 当离开这个局部作用域时,unique_lock会调用析构函数,自动解锁 } void third() { unique_lock<mutex> loc(m); while (flag2 == false) { cd_v.wait(loc); } for (int i = 0; i < 10; ++i) { cout << "third" << endl; } } }; int main() { Foo F; // thread 需要传参,尤其对类对象,函数 + 类对象的地址 thread t1(&Foo::first, &F); thread t2(&Foo::second, &F); thread t3(&Foo::third, &F); t1.detach(); t2.detach(); t3.detach(); system("pause"); } #endif // 题目3 多线程交替打印字符 #if 0 #include <iostream> #include <thread> #include <mutex> #include <condition_variable> #include <chrono> #include <functional> using namespace std; int n = 5; mutex mtx; condition_variable cv; bool foo_done = false; void foo(function<void(int)> printFoo) { for (int i = 0; i < n; i++) { unique_lock<mutex> locker(mtx); while (foo_done == true) cv.wait(locker); printFoo(i); foo_done = true; cv.notify_one(); } } void bar(function<void(int)> printBar) { for (int i = 0; i < n; i++) { unique_lock<mutex>locker(mtx); while (foo_done == false) cv.wait(locker); printBar(i); foo_done = false; cv.notify_one(); } } void printBar(int i) { cout << "bar" <<i<< endl; } void printFoo(int i) { cout << "Foo" <<i<< endl; } int main() { // thread 需要传参,尤其对类对象,函数 + 类对象的地址 thread t1(foo, printBar); thread t2(bar,printFoo); t1.detach(); t2.detach(); system("pause"); } #endif // 1
// 三个线程 顺序打印 #if 1 condition_variable cv; mutex m; int flag = 0; int n = 10; void printA() { unique_lock<mutex> loc(m); for (int i = 0; i < n; i++){ cout << "A:" << i << endl; } flag = 1; loc.unlock(); cv.notify_all(); } void printB() { unique_lock<mutex> loc(m); while (flag != 1) cv.wait(loc); for (int i = 0; i < n; i++) { cout << "B:" << i << endl; } flag = 2; loc.unlock(); cv.notify_all(); } void printC() { unique_lock<mutex> loc(m); while (flag != 2) cv.wait(loc); for (int i = 0; i < n; i++) { cout << "C:" << i << endl; } loc.unlock(); cv.notify_all(); } int main() { thread t1(printA); thread t2(printB); thread t3(printC); t1.detach(); t2.detach(); t3.detach(); system("pause"); return 0; } #endif // 1
// 三个线程 交替打印 #if 0 condition_variable cv; mutex m; int flag = 0; int n = 10; void printA() { for (int i = 0; i < n; i++) { unique_lock<mutex> loc(m); while (flag != 0) cv.wait(loc); cout << "A:" << i << endl; flag = 1; loc.unlock(); cv.notify_all(); } } void printB() { for (int i = 0; i < n; i++) { unique_lock<mutex> loc(m); while (flag != 1) cv.wait(loc); cout << "B:" << i << endl; flag = 2; loc.unlock(); cv.notify_all(); } } void printC() { for (int i = 0; i < n; i++) { unique_lock<mutex> loc(m); while (flag != 2) cv.wait(loc); cout << "C:" << i << endl; flag = 0; loc.unlock(); cv.notify_all(); } } int main() { thread t1(printA); thread t2(printB); thread t3(printC); t1.detach(); t2.detach(); t3.detach(); system("pause"); return 0; } #endif // 1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。