SafeQueue.h
//避免被多次 include #pragma once //避免头文件被多次包含 , 有两种处理方式 // ① 一种是 #ifndef A #define A #endif 方式 // ② 另一种就是 使用 #pragma once 宏 #include <queue> //引入头文件 , 需要使用互斥锁相关逻辑 #include <pthread.h> using namespace std; //创建一个模板类 , 对 Queue 进行封装 , // 保证该 queue 队列是一个线程安全的队列 // 对 queue 队列操作是线程安全的 template <typename T> class SafeQueue { public : //定义构造函数 SafeQueue() { //初始化互斥锁 pthread_mutex_init(&mutex, 0); //初始化条件变量 pthread_cond_init(&cond, 0); } //定义析构函数 ~SafeQueue() { //释放互斥锁 pthread_mutex_destroy(&mutex); //销毁条件变量 pthread_cond_destroy(&cond); } //向队列中加入元素 , 或 从队列中取出元素 // queue 队列不是线程安全的 , 现在要保证该 queue 存储元素是线程安全的 // 需要使用互斥锁控制 push ( 加入元素 ) 和 pop ( 取出元素 ) 操作 ; //向队列中加入元素 void push(T t) { //使用互斥锁将操作锁起来 pthread_mutex_lock(&mutex); //使用互斥锁 , 向队列中加入数据是安全的 safe_queue.push(t); //唤醒一个线程 , 唤醒哪个线程 是无法控制的 ; 该方法 相当于 Java 中的 notify() //pthread_cond_signal(&cond); //使用广播通知所有等待的线程 , 唤醒所有的线程 , 相当于 Java 中的 notifyAll pthread_cond_broadcast(&cond); //解除互斥锁 pthread_mutex_unlock(&mutex); } /* 现在要实现这样一个需求 : 如果 pop 方法获取时 , 该队列 q 为空 , 此时肯定获取不到数据了 但是我们规定每次调用 pop 必须获取一个数据 这样的话 , 如果检测到 pop 中没有数据 , 就必须先将线程阻塞 等到有新的元素 push 进来后 , 解除阻塞 , 使用条件变量实现 */ //从队列中取出元素 ( 无论如何都要获取到 , 如果获取不到就阻塞到能获取到的时候 ) void popAnyway(T& t) { //使用互斥锁将操作锁起来 pthread_mutex_lock(&mutex); //如果没有数据 , 那么阻塞等待数据 if (safe_queue.empty()) { //阻塞等待 , 相当于 Java 中的 wait() 方法 pthread_cond_wait(&cond, &mutex); } //如果阻塞解除 , 那么执行下面的内容 //t 参数是传入的引用 , 这里可以直接给 t 引用赋值 t = safe_queue.front(); //将首元素移除 safe_queue.pop(); //解除互斥锁 pthread_mutex_unlock(&mutex); } //从队列中取出元素 ( 取数据时要判空 ) void pop(T& t) { //使用互斥锁将操作锁起来 pthread_mutex_lock(&mutex); //使用互斥锁 , 向队列中加入数据是安全的 , 如果队列是空的 , 就获取不到元素 if (!safe_queue.empty()) { //t 参数是传入的引用 , 这里可以直接给 t 引用赋值 t = safe_queue.front(); //将首元素移除 safe_queue.pop(); } //解除互斥锁 pthread_mutex_unlock(&mutex); } private : //实际操作的队列 ( 先进先出 ) , 该队列不是线程安全的 // 如果要保证该 Queue 是线程安全的话 , 就需要为其设置一个互斥锁 // 下面的 mutex 互斥锁变量 , 就是为了保证该队列是线程安全队列而设置的 queue<T> safe_queue; //互斥锁变量 // 1. 先导入头文件 // 2. 定义互斥锁变量 // 3. 在构造函数中进行初始化 // 4. 在析构函数中释放 pthread_mutex_t mutex; //条件变量 // 使用流程 : // 1. 在构造函数中进行初始化 // 2. 在析构函数中释放 pthread_cond_t cond; };
CMakeLists.txt
# CMakeList.txt: 005_Thread 的 CMake 项目,在此处包括源代码并定义 # 项目特定的逻辑。 # cmake_minimum_required (VERSION 3.8) #引入头文件 include_directories("include") #配置自动根据当前是 32 位还是 64 位程序 , 确定静态库的配置目录 if(CMAKE_CL_64) set(platform x64) else() set(platform x86) endif() #配置静态库 , 用于引导如何链接动态库和静态库 link_directories("lib/${platform}") #处理 “timespec”:“struct” 类型重定义 报错信息 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_STRUCT_TIMESPEC") # 将源代码添加到此项目的可执行文件。 add_executable (006_ThreadSafeQueue "006_ThreadSafeQueue.cpp" "006_ThreadSafeQueue.h") #链接生成的 006_ThreadSafeQueue 和线程动态库名字 # 动态库是 lib/x64 下的 pthreadVC2.lib target_link_libraries(006_ThreadSafeQueue pthreadVC2) # TODO: 如有需要,请添加测试并安装目标。
运行结果
V . 示例代码说明
下载完项目后 , 使用 Visual Studio 打开 , 注意需要配置 POSIX 线程库 ;
【Visual Studio】Visual Studio 2019 社区版 CMakeList 开发环境安装 ( 下载 | 安装相关组件 | 创建编译执行项目 | 错误处理 )
【Visual Studio 2019】创建 导入 CMake 项目
【C++ 语言】Visual Studio 配置 POSIX 线程 ( Windows 不支持 POSIX | 配置文件下载 | 库文件说明 | 配置过程 )