基于信号量的多生产多消费环形队列
代码
cpp
const static int defaultcap=5;
template<class T>
class RingQueue
{
private:
void P(sem_t &sem)
{
sem_wait(&sem); //资源-1
}
void V(sem_t &sem)
{
sem_post(&sem); //资源加一
}
void Lock(pthread_mutex_t &mutex)
{
pthread_mutex_lock(&mutex);
}
void Unlock(pthread_mutex_ &mutex)
{
pthread_mutex_unlock(&mutex);
}
public:
RingQueue(int cap=defaultcap)
:ringqueue_(cap),cap_(cap),c_step_(0),p_step_(0)
{
sem_init(&cdata_sem_,0,0); //初始化信号量
sem_init(&pspace_sem_,0,cap_); //初始化信号量
pthread_mutex_init(&c_mutex_,nullptr);
pthread_mutex_init(&c_mutex_,nullptr);
}
void Push(const T&in) //生产
{
//先申请信号量后申请锁
//能让两个申请并行,提高并发度
//申请信号量是原子的
P(pspace_sem_);
Lock(p_mutex_);
ringqueue_[p_step_]=in;
// 位置后移,维持环形特征
p_step_++;
p_step_%=cap_;
Unlock(p_mutex_);
V(cdata_sem_);
}
void Pop(T* out) //消费
{
P(cdata_sem_);
Lock(c_mutex_);
*out=ringqueue_[c_step_];
// 位置后移动
c_step_++;
c_step_%=cap_;
Unlock(c_mutex_);
V(pspace_sem_);
}
~RingQueue()
{
sem_destroy(&cdata_sem_); //释放信号量资源
sem_destroy(&pspace_sem_);
pthread_mutex_destroy(&c_mutex_);
pthread_mutex_destroy(&p_mutex_);
}
private:
std::vector<T> ringqueue_;
int cap_;
int c_step_; //消费者下标
int p_step_; // 生产者下标
// 使用信号量可以自动维护互斥性
sem_t cdata_sem_; //消费者关注的数据资源
sem_t pspace_sem_; // 生产者关注的空间资源
pthread_mutex_t c_mutex_; // 多个生产者竞争对信号量的申请
pthread_mutex_t p_mutex_; // 多个消费者竞争对信号量的申请
};
信号量保证生产者和消费者的同步关系
多线程就要上锁
问题思考
当多线程访问时如何,锁和信号量谁在前?
信号量申请在前,原因可以使得申请信号量和申请锁行为同步,申请信号量是原子的
当多个线程持有信号量时,会不会对消费资源进行误导?
不会,因为资源的消费取决于cdata_sem_,而当释放c_data_sem此时的确实已经表明资源量加1
当不会出现队列只剩下一个空位,但是有多个线程持有信号量?
不会,因为信号量和空位一一对应的,如果能申请到信号量说明确实有空位
线程池
接收任务,线程池里面的多个线程分配任务
代码
cpp
#pragma once
#include<iostream>
#include<pthread.h>
#include<vector>
#include<queue>
#include<string>
#include<unistd.h>
struct ThreadInfo
{
pthread_t tid;
std::string name;
};
static const int defaultnum=5;
template<class T>
class ThreadPool
{
private:
void Lock()
{
pthread_mutex_lock(&mutex_);
}
void Unlock()
{
pthread_mutex_unlock(&mutex_);
}
void Wakeup() //唤醒进程
{
pthread_cond_signal(&cond_);
}
void ThreadSleep()
{
pthread_cond_wait(&cond_,&mutex_);
}
bool IsQueueEmpty()
{
return tasks_.empty();
}
public:
ThreadPool(int num=defaultnum)
:threads_(num)
{
pthread_mutex_init(&mutex_,nullptr);
pthread_cond_init(&cond_,nullptr);
}
//HandlerTask放在类里面会多出一个指针,要加上static
static void* HandlerTask(void*args)
{
ThreadPool<T>*tp=static_cast<ThreadPool<T>*>(args);
while(true)
{
tp->Lock();
while(tp->IsQueueEmpty())
{
tp->ThreadSleep();
}
// T t=tp->Pop();
tp->Unlock();
// t(); //处理任务的同时不涉及共享资源,可以并发指行
}
}
//初始化,创建多个线程
void Start()
{
int num=threads_.size();
for(int i=0;i<num;i++)
{
threads_[i].name="thread-"+std::to_string(i+1);
pthread_create(&(threads_[i].tid),nullptr,HandlerTask,this);
}
}
void Push(const T&t)
{
Lock();
tasks_.push(t);
Wakeup();
Unlock();
}
T Pop()
{
T t=tasks_.front();
tasks_.pop();
return t;
}
~ThreadPool()
{
pthread_mutex_destroy(&mutex_);
pthread_cond_destroy(&cond_);
}
private:
std::vector<ThreadInfo> threads_;
std::queue<T> tasks_;
pthread_mutex_t mutex_;
pthread_cond_t cond_;
};
问题思考
在类里面调用HandlerTask函数
封装线程
代码
cpp
#pragma once
#include<iostream>
#include<string>
#include<ctime>
#include<pthread.h>
typedef void (*callback_t)();
static int num=1;
class Thread
{
public:
static void *Routine(void*args)
{
Thread* thread=static_cast<Thread*>(args);
thread->Entery();
return nullptr;
}
public:
Thread(callback_t cb)
:tid_(0)
,name_("")
,start_timestamp_(0)
,isrunning_(false)
,cb_(cb)
{
}
void Run()
{
name_="thread-" + std::to_string(num++);
start_timestamp_=time(nullptr);
isrunning_=true;
pthread_create(&tid_,nullptr,Routine,this);
}
void Join()
{
pthread_join(tid_,nullptr);
isrunning_=false;
}
std::string Name();
uint64_t StartTimestamp();
bool IsRunning()
{
return isrunning_;
}
void Entery()
{
cb_();
}
~Thread()
{
}
private:
pthread_t tid_; //线程tid
std::string name_; //线程名字
uint64_t start_timestamp_; //线程启动时间
bool isrunning_; // 线程是否运行
callback_t cb_; //回调函数
};