C++|[C++] C++11中的线程库


文章目录

  • 线程库
  • 常用接口
  • join()和deatch()
  • 原子性操作库

线程库 在C++11之前,涉及到多线程问题,都是和平台相关的,比如windows和linux下各有自己的接口,这使得代码的可移植性比较差。
C++11中最重要的特性就是对线程进行支持了,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念。要使用标准库中的线程,必须包含< thread >头文件。
常用接口
函数名 功能
thread() 构造一个线程对象,没有关联任何线程函数,即没有启动任何线程
thread(fn, args1, args2, …) 构造一个线程对象,并关联线程函数fn,args1,args2,…为线程函数的参数
get_id() 获取线程id
jionable() 用来判断是否能够调用join()或者detach(),可以返回true,不可以返回false.joinable代表的是一个正在执行中的线程。
jion() 该函数调用后会阻塞住线程,当该线程结束后,主线程继续执行
detach() 在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离的线程变为后台线程,创建的线程的"死活"就与主线程无关
注意:
  1. 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态。
  2. 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。
C++|[C++] C++11中的线程库
文章图片

get_id()的返回值类型为id类型,id类型实际为std::thread命名空间下封装的一个类,该类中包含了一个
结构体:
// vs下查看 typedef struct { /* thread identifier for Win32 */ void *_Hnd; /* Win32 HANDLE */ unsigned int _Id; } _Thrd_imp_t;

  1. 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。线程函数一般情况下可按照以下三种方式提供:函数指针,lambda表达式,仿函数对象
#include #include #includeusing namespace std; void fun1() { cout << "t1" << endl; }struct fun2 { void operator()() { cout << "t2" << endl; } }; int main() { // 线程函数为函数指针 thread t1(fun1); // 线程函数为函数对象 fun2 a; thread t2(a); // 线程函数为lambda表达式 thread t3([] {cout << "t3" << endl; }); //线程等待,回收资源 t1.join(); t2.join(); t3.join(); return 0; }

【C++|[C++] C++11中的线程库】C++|[C++] C++11中的线程库
文章图片

  1. thread类是防拷贝的,不允许拷贝构造以及赋值,但是可以移动构造和移动赋值,即将一个线程对象关联线程的状态转移给其他线程对象,转移期间不影响线程的执行。
  2. 可以通过jionable()函数判断线程是否是有效的,如果是以下任意情况,则线程无效
    ?采用无参构造函数构造的线程对象
    ?线程对象的状态已经转移给其他线程对象
    ?线程已经调用jion或者detach结束
  3. 在线程函数中对形参进行修改,不会影响外部实参,因为:线程函数参数虽然是引用方式,但其实际引用的是线程栈中的拷贝
join()和deatch() join()和deatch()都是用来会回收线程资源的,但是每个线程只能用一个方式,用了join就不能用deatch否则程序会崩溃
join(): 当新线程终止时,join()会清理相关的线程资源,然后返回,主线程再继续向下
执行,然后销毁线程对象。由于join()清理了线程的相关资源,thread对象与已销毁的线程就没有关系了,因此一个线程对象只能使用一次join(),否则程序会崩溃。
deatch(): 该函数被调用后,新线程与线程对象分离,不再被线程对象所表达,就不能通过线程对象控制线程了,新线程会在后台运行,其所有权和控制权将会交给c++运行库。同时,C++运行库保证,当线程退出时,其相关资源的能够正确的回收。
原子性操作库 C++11如何解决多线程中的线程安全问题?
  1. 加锁: 加锁虽然可以保证安全,但是会影响运行效率还容易造成死锁问题,所以使用这种方法要注意检查
  2. 原子操作: 原子操作是指, 不能打断的一个或者一系列操作,C++11印入原子操作类型,使得线程间的数据同步变得非常高效
对比下面两段代码:
未使用原子操作:
#include #include #include using namespace std; unsigned long sum = 0L; void fun(size_t num) { for (size_t i = 0; i < num; ++i) sum++; } int main() { cout << "Before joining,sum = " << sum << std::endl; thread t1(fun, 10000000); thread t2(fun, 10000000); t1.join(); t2.join(); cout << "After joining,sum = " << sum << std::endl; return 0; }

未使用原子操作,运行结果明显错误
C++|[C++] C++11中的线程库
文章图片

使用原子操作:
#include #include #include using namespace std; atomic_long sum{ 0 }; //atomic_long为原子类型void fun(size_t num) { for (size_t i = 0; i < num; ++i) sum++; // 原子操作 } int main() { cout << "Before joining, sum = " << sum << std::endl; thread t1(fun, 1000000); thread t2(fun, 1000000); t1.join(); t2.join(); cout << "After joining, sum = " << sum << std::endl; return 0; }

C++|[C++] C++11中的线程库
文章图片

    推荐阅读