在C++11新标准中,可以简单通过使用thread库,来管理多线程,使用时需要#include <thread>
头文件。
简单用例如下:
1 std::thread(Simple_func); 2 std::thread t(Simple_func); 3 t.detach();
第一行是直接启动一个新线程来执行Simple_func函数,而第二行先声明一个线程函数t(返回类型为thread),然后用detach方法启动线程。
C++11有两种方式来等待线程结束:
- detach方式,启动的线程自主在后台运行,当前的代码继续往下执行,不等待新线程结束。前面代码所使用的就是这种方式。
- 调用detach表示thread对象和其表示的线程完全分离;
- 分离之后的线程是不在受约束和管制,会单独执行,直到执行完毕释放资源,可以看做是一个daemon线程;
- 分离之后thread对象不再表示任何线程;
- 分离之后joinable() == false,即使还在执行;
- join方式,等待启动的线程完成,才会继续往下执行。假如前面的代码使用这种方式,其输出就会0,1,2,3,因为每次都是前一个线程输出完成了才会进行下一个循环,启动下一个新线程。
- 只有处于活动状态线程才能调用join,可以通过joinable()函数检查;
- joinable() == true表示当前线程是活动线程,才可以调用join函数;
- 默认构造函数创建的对象是joinable() == false;
- join只能被调用一次,之后joinable就会变为false,表示线程执行完毕;
- 调用 ternimate()的线程必须是 joinable() == false;
- 如果线程不调用join()函数,即使执行完毕也是一个活动线程,即joinable() == true,依然可以调用join()函数;
向线程传递参数
向线程调用的函数传递参数也是很简单的,只需要在构造thread
的实例时,依次传入即可。例如
int Simple_func(int a, int b); std::thread t(Simple_func,1,2);
需要注意的是,默认的会将传递的参数以拷贝的方式复制到线程空间,即使参数的类型是引用,如果在线程中使用引用来更新对象时,就需要注意了。默认的是将对象拷贝到线程空间,其引用的是拷贝的线程空间的对象,而不是初始希望改变的对象。例如:
int ChangeNum(int &a); int num=0; std::thread t(ChangeNum,num); t.join();
在线程内,将对象的字段a和b设置为新的值,但是在线程调用结束后,这两个字段的值并不会改变。这样由于引用的实际上是局部变量num的一个拷贝。
若想通过线程改变对象值,需调用std::ref
,将num
的引用传入线程,如:
std::thread t(ChangeNum,std::ref(num));
thread
是可移动的(movable)的,但不可复制(copyable)。可以通过move
来改变线程的所有权,灵活的决定线程在什么时候join或者detach。
std::thread也可以去包装一个类,前提是该类对()操作符进行了重载,使其相当于拥有了函数的性质。(此处类似于std::bind的绑定)
在多线程编程的时候,资源竞争是很常见的问题,因此需要引入互斥锁。c++11中提供了std::mutex,而在C++17开始,标准库提供了shared_mutex类。
对于shared_mutex,可以理解为共享锁,允许多个线程同时对同一资源进行读操作。而lock_guard、unique_lock可以理解为独占锁,只允许一个线程对资源进行写操作。
在一些只读函数中可以用std::shared_mutex,而在写操作函数中需用std::unique_lock。
std::shared_mutex是c++17中引入的,不支持std::mutex,需用std::shared_mutex声明互斥信号量。
参考文章:https://immortalqx.github.io/2021/12/04/cpp-notes-3/
标签:std,多线程,join,thread,调用,mutex,线程,unique From: https://www.cnblogs.com/Explosion556/p/17275743.html