文章目录
- 线程库
- 常用接口
- 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() | 在创建线程对象后马上调用,用于把被创建线程与线程对象分离开,分离的线程变为后台线程,创建的线程的"死活"就与主线程无关 |
- 线程是操作系统中的一个概念,线程对象可以关联一个线程,用来控制线程以及获取线程的状态。
- 当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。
文章图片
get_id()的返回值类型为id类型,id类型实际为std::thread命名空间下封装的一个类,该类中包含了一个
结构体:
// vs下查看
typedef struct
{ /* thread identifier for Win32 */
void *_Hnd;
/* Win32 HANDLE */
unsigned int _Id;
} _Thrd_imp_t;
- 当创建一个线程对象后,并且给线程关联线程函数,该线程就被启动,与主线程一起运行。线程函数一般情况下可按照以下三种方式提供:
函数指针,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中的线程库】
文章图片
- thread类是防拷贝的,不允许拷贝构造以及赋值,但是可以移动构造和移动赋值,即将一个线程对象关联线程的状态转移给其他线程对象,转移期间不影响线程的执行。
- 可以通过jionable()函数判断线程是否是有效的,如果是以下任意情况,则线程无效
?采用无参构造函数构造的线程对象
?线程对象的状态已经转移给其他线程对象
?线程已经调用jion或者detach结束
- 在线程函数中对形参进行修改,不会影响外部实参,因为:线程函数参数虽然是引用方式,但其实际引用的是线程栈中的拷贝
join(): 当新线程终止时,join()会清理相关的线程资源,然后返回,主线程再继续向下
执行,然后销毁线程对象。由于join()清理了线程的相关资源,thread对象与已销毁的线程就没有关系了,因此一个线程对象只能使用一次join(),否则程序会崩溃。
deatch(): 该函数被调用后,新线程与线程对象分离,不再被线程对象所表达,就不能通过线程对象控制线程了,新线程会在后台运行,其所有权和控制权将会交给c++运行库。同时,C++运行库保证,当线程退出时,其相关资源的能够正确的回收。
原子性操作库 C++11如何解决多线程中的线程安全问题?
- 加锁: 加锁虽然可以保证安全,但是会影响运行效率还容易造成死锁问题,所以使用这种方法要注意检查
- 原子操作: 原子操作是指, 不能打断的一个或者一系列操作,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;
}
未使用原子操作,运行结果明显错误使用原子操作:
文章图片
#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++11)
- C++语言|【C++碎碎念】C++11新特性(声明、智能指针、右值引用、lambda表达式)
- 精通高并发系列|多年亿级流量下的高并发经验总结,我毫无保留的写在了这本书中(CSDN创始人、总裁、副总裁联合推荐)
- 数据结构|背包问题求解(数据结构课设)
- 数据结构|教学计划编制问题(数据结构课程设计)
- Linux|Linux进程信号
- c++学习|17.7 单例设计模式共享数据分析、解决与call_once
- c++学习|17.8 condition_variable、wait、notify_one与notify_all
- c++学习|17.6 unique_lock详解