队列中如果同时存在pop和push操作,很容易出现冲突,所以多线程操作引入了锁,为了更好的保障在队列为空时,释放CPU,又引入了条件变量。
以下SafeQueue.h文件中,为pop和push分别引入了锁和条件变量。
#ifndef FFMPEGS_SAFE_QUEUE_H
#define FFMPEGS_SAFE_QUEUE_H
#pragma once
#include <queue>
#include <pthread.h>
using namespace std;
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);
}
void push(T t){
pthread_mutex_lock(&mutex); //加锁
q.push(t);
// 通知变化 notify
// 由系统唤醒一个线程
pthread_cond_signal(&cond);
// 通知所有的线程
// pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex); //操作完成后解锁
}
void pop(T& t){
pthread_mutex_lock(&mutex); //加锁
// if(!q.empty()){
// t = q.front();
// q.pop(); //没有返回值,所以使用q.front()拿到首元素 q.back()返回最后一个元素,
// }
// queue为空是一直等待,直到下一次push进新的数据 java中是wait和notify
if(q.empty()) {
// 挂起状态,释放锁
pthread_cond_wait(&cond, & mutex);
}
// 被唤醒以后
t = q.front();
q.pop();
pthread_mutex_unlock(&mutex); //操作完成后解锁
}
private:
// 如何保证对这个队列的操作是线程安全的?引入互斥锁
queue<T> q;
pthread_mutex_t mutex;
// 创建条件变量
pthread_cond_t cond;
};
#endif //FFMPEGS_SAFE_QUEUE_H
在main文件中,开始执行
SafeQueue<int> sq;
void* get(void *){
while (1)
{
int i;
sq.pop(i);
cout<<"取出来的数据是:"<<i<<endl;
}
}
void *put(void *){
while (1)
{
int i;
// 将用户输入保存给i;
cin>>i;
sq.push(i);
}
}
int main(){
pthread_t pid1,pid2;
pthread_create(&pid1, 0,get,0);
pthread_create(&pid2, 0,put,0);
pthread_join(pid1,0);
system("pause");
}
在这个程序中,需要重点关注的是,
1、线程的创建 pthread_t 得到线程变量,pthread_create()中传入线程变量地址和线程执行的函数。
2、私有的锁变量:pthread_mutex_t mutex; 锁的启动和销毁
pthread_mutex_init(&mutex, 0);
pthread_mutex_destory(&mutex);
在方法的执行前,增加锁,执行完,释放锁。
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
3、线程的条件变量 pthread_cond_t cond; 线程变量的初始化和销毁
pthread_cond_init(&cond, 0);
pthread_cond_destory(&cond);
在需要wait的地方执行pthread_cond_wait(&cond, &mutex);
在满足条件的地方执行类似notify的方法
pthread_cond_signal(&cond);
或者 pthread_cond_broadcase(&cond);通知wait方法继续执行。