C++_线程池解读-线程和调试以及构建

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
posted @ 2024-12-10 15:09  辰令  阅读(301)  评论(0)    收藏  举报