C++11多线程相关的头文件
C++11 新标准中引入了四个头文件来支持多线程编程,
他们分别是< atomic> ,< thread>,< mutex>,< condition_variable>和< future>
作用:
线程池这层封装,只需要告诉它开启几个线程,然后直接塞任务就行了,然后通过一定的机制获取执行结果
线程池可以想象成一个池子,它的作用就是让每一个线程结束后,并不会销毁,
而是放回到线程池中成为空闲状态,等待下一个对象来使用
内部功能
线程池有以下几个部分:
1.完成主要任务的一个或多个线程.
2.用于调度管理的管理线程.
3.要求执行的任务队列
解读
用到的库
thread线程相关,mutex 互斥量,解决资源抢占问题,
condition_variable 条件量,用于唤醒线程和阻塞线程,
future 从使用的角度出发,它是一个获取线程数据的函数。
functional 函数子,可以理解为规范化的函数指针。stdexcept 就跟它的名字一样,标准异常
功能 5个成员变量 3个成员函数
线程池的声明,构造函数,一个enqueue模板函数 返回std::future<type>,
workers 是vector<std::thread> 俗称工作线程。
std::queue<std::function<void()>> tasks 俗称任务队列
mutex 互斥量,解决资源抢占问题,
condition_variable 条件量,用于唤醒线程和阻塞线程,
stop 控制线程池停止的
代码:
将lamda表达式推给线程队列,用于线程执行的对象 , std::thread 的构造函数可以接收一个可调用对象,
如函数指针、函数对象、或是一个 lambda 表达式。
lamda内部就是执行tasks的任务队列里的任务
向任务队列里添加任务
线程池的enqueue就是将任务推进去,equeue的返回是future
语法理解
result_of 是一个模板类
创建线程的方式为:std::thread t(fun);
述workers.emplace_back()中,我们传入的lambda表达式就是创建线程的fun()函数
[ 捕获 ] ( 形参 ) 说明符(可选) 异常说明 attr -> 返回类型 { 函数体 }
[ 捕获 ] { 函数体 } 类型。
先创建一个封装void()函数的std::function对象task,用于接收后续从任务队列中弹出的真实任务
执行报错
项目里加了所需要的头文件 #include <pthread.h> 但编译却报对pthread_create未定义的引用的错误
对‘pthread_create’未定义的引用 根本原因是没有包含所需要的库
pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,
pthread 库是 POSIX 线程(Portable Operating System Interface for uniX threads)库的简称
解决方式
1. 一是通过项目属性添加库依赖 CMakeLists文件:link_libraries(pthread)
2. gcc的解决方式
gcc xxx.c -o yyy -lpthread
-l :是『加入某个函式库(library)』的意思
Linux 默认是将库放置在 /lib 与 /usr/lib 当中
pthread 库不是 Linux 系统默认的库,连接时需要使用静态库 libpthread.a
/usr/include/pthread.h
原生线程库怎么一回事:
Linux无法提供有关线程的系统调用接口,它只能提供轻量级进程的系统调用接口;
而用户(程序员)只需要线程,所以Linux被迫封装出了一个库,即pthread原生线程库,
该库与系统调用有区别,它是一个处于用户层的库;
那么使用gcc/g++编译器时,它只能认识系统调用接口和语言本身自带的库,第三方库则不认识,
所以在使用pthread原生线程库时,必须指明需要链接的库,即添加[-l pthread]选项
排查方式
whereis libpthread
whereis pthread
###变量 文件 命令
`LD_LIBRARY_PATH` 是一个环境变量,用于指定动态链接器搜索共享库(.so 文件)时的额外路径
LD_LIBRARY_PATH 指定动态库搜索路径
export LD_LIBRARY_PATH="LD_LIBRARY_PATH:/mylibpath/"
在编译目标代码时指定该程序的动态库搜索路径
cpp -v 命令查看gcc的默认系统头文件路径
gcc -v
cat /etc/ld.so.conf
cat /etc/ld.so.conf.d/libc.conf
cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf
ldd 可执行程序 查看链接的库
头文件和库文件共同确保了函数的正确调用和执行
头文件的作用:头文件(.h文件)主要用于提供函数的声明和宏定义等。它告诉编译器函数或宏的存在,但不包含具体的实现代码
库文件的作用:库文件(如静态库.a和动态库.so)则包含了函数的实际实现代码。
第一步:需要使用静态库的地方引入静态库对应的头文件
第二步:编译链接的时候指定链接库的位置
Cmake
find_library(<VAR> name1 [path1 path2 ...]) name1是要查找的库文件的名称,不带"lib"前缀。
find_library函数会按照以下顺序查找库文件:
在指定的路径中查找库文件。
在系统默认的库文件路径中查找库文件。
NO_DEFAULT_PATH 选项告诉 CMake 不要使用系统默认的路径进行查找,只在 /usr/local/lib 路径中查找 mylibrary
link_libraries 将库链接到稍后添加的所有目标
target_link_libraries 将一个或多个库链接到特定的目标
link_libraries 作为 全局链接库设置,需要在add_library或者add_executable命令之前使用
target_link_libraries 为特定的目标指定链接库,则需要在add_library或者add_executable命令之后使用
或者其
在 CMakeLists.txt 文件中使用 find_library 来查找静态库:
find_library(PTHREAD_LIB pthread PATHS "/usr/lib/x86_64-linux-gnu")
确保在 target_link_libraries 中使用找到的库:
add_executable(your_executable your_source_file.cpp)
target_link_libraries(your_executable ${PTHREAD_LIB})
线程池
//任务添加完之后,通知阻塞线程过来消费任务,有点像生产消费者模型
workThread 工作线程中的线程具体要做什么呢?进入线程的时候必要
用unique_lock进程加锁处理,不能让其他线程以及主线程影响到要处理的这个线程。
判断任务队列是否为空,
如果为空,则利用条件变量中的wait函数来阻塞该线程,
等待任务队列不为空之后唤醒它。然后取出任务队列中的任务,执行任务中的具体操作
任务队列 taskQueue
append()就像是生产者,不断的将任务放入队列,
run()函数就像消费者,不断的从任务队列中取出任务来处理,
生产消费的两头分别用notify_one()和wait()来唤醒和阻塞
C++多线程
线程开发的最基本概念主要包含三点:线程,互斥锁,条件。
其中,
线程 thread 操作又分线程的创建,退出,等待 3 种。
互斥锁 mutex 则包括 4 种操作,分别是创建,销毁,加锁和解锁。
条件操作 condition_variable 有 5 种操作:创建,销毁,触发,广播和等待
利用条件变量和互斥锁来实现,模型可以参考生产消费者模型
condition_variable
condition_variable(条件变量)是 condition_variable 有三个等待函数:wait()、wait_for() 和 wait_util()
C++11 中提供的一种多线程同步机制,它允许一个或多个线程等待另一个线程发出通知,以便能够有效地进行线程同步
与互斥锁一起使用,以互斥的方式访问共享资源,并阻塞线程,等待通知
mutex(互斥锁)
当线程需要等待某个条件变成真时,它会获取一个互斥锁,然后在条件变量上等待,等待期间会自动释放互斥锁。
另一个线程在满足条件后会获取相同的互斥锁,
并调用条件变量的 notify_one() 或 notify_all() 函数来唤醒等待的线程。
与互斥锁一起使用,以互斥的方式访问共享资源,并阻塞线程,等待通
mutex 是一种用于保护共享资源不被多个线程同时访问的同步机制
std::mutex 类来实现互斥锁 对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。
mutex类4种
两种锁: std::lock_guard std::unique_lock
std::thread 谁调用了这个函数? 在什么线程环境下调用了这个函数?
join()
线程控制流程
线程创建(pthread_create) fork()
线程退出(pthread_exit)
线程等待(pthread_join)
线程脱离(pthread_detach)
线程ID获取(pthread_self) getpid()
其他数据结构
#include<vector> vector是向量类型,可以看作一个动态的数组
向向量中添加元素 从向量中读取元素
##queue 队列
先进先出(FIFO, First In First Out)
入队(enqueue)和出队(dequeue)
push:向队列的尾部插入元素,对应的就是入队操作 emplace:在队列的尾部构造元素,对应的也是入队的操作
pop:删除队列首个元素,对应的就是出队操作
front:访问队列第一个元素
##stack 栈
栈(Stack) 先进后出(FILO, First In Last Out)
参考
https://github.com/progschj/ThreadPool
解读github上流行的ThreadPool源码 https://www.cnblogs.com/chenleideblog/p/12915534.html
C++11实现的 100行线程池 解析 https://segmentfault.com/a/1190000022456590
C++11多线程编程(六)——线程池的实现 https://cloud.tencent.com/developer/article/2378297
Linux系统编程——多线程[上]:线程概念和线程控制 https://www.cnblogs.com/lyyyds/articles/17351135.html