1.C++11的标准库中的多线程库 #include
2.创建线程的格式 : 关键字thread 线程名(入口函数,入口函数的参数);
若入口函数参数比较多,按照顺序往后写,用英文逗号隔开。
3.子线程开始运行的两张方式:
- join() 子线程阻塞主线程,子线程运行完之后,主线程再次开始运行;
- detach() 主线程和子线程同时运行,如果主线程需要使用子线程的数据,在用到子线程数据的地方,子线程还没有运行结束,那么这个时候主线程使用的数据还是子线程最初的数据,由此会产生一些错误,不太建议使用这种方式。
例如:
- 入口函数:void fun(int a);
- 创建线程:thread mythread(fun,a);
#include互斥量#include #include using namespace std; void fun(int a) { cout << "子线程id为" << this_thread::get_id() << endl; cout << "子线程睡眠5秒" << endl; Sleep(5000); } int main() { int a = 10; thread mythread(fun, 10); cout << "主线程中的子线程id:" << mythread.get_id() << endl; cout << "子线程加入:" << endl; mythread.join();//主线程被阻塞直至子线程执行结束 //mythread.detach();//主线程子线程分别执行 cout << "子线程结束,返回主线程"<< endl; return 0; }
1.c++中互斥量是一个类,我们可以理解成是一把锁,在一些需要互斥使用的临界区(代码段)加上锁,锁住的代码区域要适中,太少起步到保护效果,太多会影响运行效率;
2.互斥量的头文件 #include
3.互斥量的创建 mutex m; 这个m是创建的类对象,mutex为类名;
4.lock()和unlock()要成对使用;
#include#include #include #include using namespace std; mutex m; int a = 1; void fun1() { m.lock(); cout << "thr1拿到锁:" << endl; cout << "原参数值" << a << endl; cout << "现参数值" << ++a << endl; m.unlock(); } void fun2() { m.lock(); cout << "thr2拿到锁:" << endl; cout << "原参数值" << a << endl; cout << "现参数值" << --a << endl; m.unlock(); } int main() { thread thr1(fun1); thread thr2(fun2); //下面两个线程同时加入,但是执行顺序可能thr1在前,也可能thr2在前 //所以执行结果也有多种 //thr1拿到锁: //原参数值1 //现参数值2 //thr2拿到锁: //原参数值2 //现参数值1 //或 //thr2拿到锁: //原参数值1 //现参数值0 //thr1拿到锁: //原参数值0 //现参数值1 thr1.join(); thr2.join(); return 0; }
为避免lock()之后忘记unlock(),造成临界区一直被锁,产生了类似于智能指针的两种方式
lock_guard1.原理:构造函数中lock(),析构函数中unlouck();
2.可以通过使用{}来调整lock()的作用域范围,让互斥量m在合适的地方被解锁
3.使用lock_guard后不能手动lock()与手动unlock();如果lock_guard加了adopt_lock参数,表示构造函数中不再加锁,需要提前手动加锁。用std::adopt_lock的前提是,自己需要先把mutex lock上;
例如:m.lock();//手动锁定,m为mutex的类对象
lock_guard
4. lock_guard不可以复制也不可以转移(move)
#includeunique_lock#include #include #include using namespace std; mutex m; int a = 1; void fun1() { lock_guard lockg1(m); cout << "thr1拿到锁:" << endl; cout << "原参数值" << a << endl; cout << "现参数值" << ++a << endl; } void fun2() { m.lock();//先手动锁定,才能再使用unique_lock的adopt_lock参数 lock_guard lockg2(m, adopt_lock); cout << "thr2拿到锁:" << endl; cout << "原参数值" << a << endl; cout << "现参数值" << --a << endl; } int main() { thread thr1(fun1); thread thr2(fun2); //下面两个线程同时加入,但是执行顺序可能thr1在前,也可能thr2在前 //所以执行结果也有多种 //thr1拿到锁: //原参数值1 //现参数值2 //thr2拿到锁: //原参数值2 //现参数值1 //或 //thr2拿到锁: //原参数值1 //现参数值0 //thr1拿到锁: //原参数值0 //现参数值1 thr1.join(); thr2.join(); return 0; }
1.于功能上unique_lock类似于lock_guard,但是unique_lock的功能更强大一些,用法上也更灵活一些,但是比lock_guard要慢一些,内存占用多一些;
2.与lock_guard不同的是,使用unique_lock后可以手动lock()与手动unlock();
3.unique_lock的参数有adopt_lock、try_to_lock、defer_lock
- adopt_lock:用std::adopt_lock的前提是,自己需要先把mutex lock上;
- try_to_lock:尝试加锁,尝试失败就立即返回,用这个try_to_lock的前提是你自己不能先lock
- defer_lock:初始化了一个没有加锁的mutex,用std::defer_lock的前提是,你不能自己先lock,否则会报异常
4. unique_lock的成员函数 lock()、unlock()、try_lock()、release()
lock():加锁
lunlock():解锁
try_lock():尝试给互斥量加锁,如果拿不到锁,返回false,如果拿到了锁,返回true,这个函数是不阻塞的
release():返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系。
5. unique_lock不可以复制,可以把所有权转移给其他锁(move)
例如:
unique_lock
unique_lock
#include#include #include #include using namespace std; mutex m; int a = 1; void fun1() { unique_lock lockg1(m, defer_lock);//初始化一个没有加锁的mutex lockg1.lock();//unique_lock加锁,不是m.lock(); cout << "fun1拿到m锁:" << endl; cout << "原参数值" << a << endl; cout << "现参数值" << ++a << endl; //lockg1.unlock(); } void fun2() { unique_lock lockg2(m, try_to_lock);//尝试拿锁,只尝试一次,如果没拿到就返回,不会导致本线程一直阻塞在lock这里。 if (lockg2.owns_lock()) { cout << "fun2拿到m锁:" << endl; cout << "原参数值" << a << endl; cout << "现参数值" << --a << endl; } else { cout << "fun2没有拿到m锁" << endl; } } int main() { thread thr1(fun1); thread thr2(fun2); //下面两个线程同时加入,但是执行顺序可能thr1在前,也可能thr2在前 //所以执行结果也有多种: //或 //fun2没有拿到m锁fun1拿到m锁: //原参数值 //1 //现参数值2 //或 //fun1拿到m锁:fun2没有拿到m锁 //原参数值1 //现参数值2 thr2.join(); thr1.join(); return 0; }



