这里没有对信号量进行封装,直接使用了原生库,读者可自行尝试封装,本文代码使用了C++11及以后的语法,智能指针,lambda等,可以尝试自行学习。
test.cc
#include"Thread.hpp"
#include"Ringqueue.hpp"
#include<iostream>
#include<memory>
#include<unistd.h>
#include<functional>
using namespace std;
void Task()
{
char name16;
int ret = pthread_getname_np(pthread_self(), name, sizeof(name));
cout<<"我是一个任务处理我的是"<<name<<endl;
}
using task=function<void()>;
int main()
{
unique_ptr<RingQueue<task>> _queue=make_unique<RingQueue<task>>();
auto productor=\&_queue(){
while(true)
{
sleep(1);
_queue->Push(Task);
}
};
auto consumer=\&_queue(){
while(true)
{
task t;
sleep(1);
_queue->Pop(&t);
t();
}
};
//p是生产者
Thread p1(productor);
Thread p2(productor);
Thread p3(productor);
//c是消费者
Thread c1(consumer);
Thread c2(consumer);
p1.start();
p2.start();
p3.start();
c1.start();
c2.start();
p1.join();
p2.join();
p3.join();
c1.join();
c2.join();
return 0;
}
Thread.hpp
#ifndef THREAD_HPP
#define THREAD_HPP
#include<iostream>
#include<functional>
#include<thread>
#include<string>
#include<unistd.h>
using namespace std;
using func_t=function<void()>;
int gnum=5;
class Thread
{
private:
static void *run(void *argc)
{
Thread *ts=(Thread *)argc;
ts->_func();
}
public:
Thread(func_t t)
:_joinable(false),
_func(t)
{
_name="Thread-"+to_string(gnum--);
}
void start()
{
pthread_create(&_tid,nullptr,&run,this);
pthread_setname_np(_tid,_name.c_str()); //在线程创建的时候设置姓名
_joinable=true;
}
void stop()
{
pthread_cancel(_tid);
}
void join() //阻塞,当线程刚刚创建的时候是可以阻塞的。
{
if(_joinable)
{
pthread_join(_tid,NULL);
}
}
void detach() //分离,当线程可以阻塞就可以分离,但是分离了不能再次阻塞了
{
if (_joinable)
{
pthread_detach(_tid);
_joinable = false;
}
}
~Thread()
{
detach();
}
private:
pthread_t _tid;
string _name;
bool _joinable;
func_t _func;
};
#endif
Ringqueue.hpp
#ifndef RINGQUEUE_HPP
#define RINGQUEUE_HPP
#include<iostream>
#include<vector>
#include<semaphore.h>
#include"Mutex.hpp"
using namespace std;
template<class T>
class RingQueue
{
public:
RingQueue(int cap=5)
:_cap(cap)
,arr(cap)
,p_step(0)
,c_step(0)
{
sem_init(&space_sem,0,cap);
sem_init(&data_sem,0,0);
}
void Push(T in)
{
sem_wait(&space_sem); //p操作,申请信号量
_p_mutex.Lock();
arrp_step=in;
p_step++;
p_step%=_cap;
sem_post(&data_sem); //v操作,释放信号量
_p_mutex.Unlock();
}
void Pop(T *out)
{
sem_wait(&data_sem); //p操作,申请信号量
_c_mutex.Lock();
*out=arrc_step;
c_step++;
c_step%=_cap;
sem_post(&space_sem); //v操作,释放信号量
_c_mutex.Unlock();
}
~RingQueue()
{
sem_destroy(&space_sem);
sem_destroy(&data_sem);
}
private:
int _cap;
vector<T> arr;
sem_t space_sem;
sem_t data_sem;
int p_step;
int c_step;
Mutex _p_mutex;
Mutex _c_mutex;
};
#endif
Mutex.hpp
#ifndef MUTEX_HPP
#define MUTEX_HPP
#include<iostream>
#include<pthread.h>
using namespace std;
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_lock,nullptr);
}
void Lock()
{
pthread_mutex_lock(&_lock);
}
void Unlock()
{
pthread_mutex_unlock(&_lock);
}
pthread_mutex_t* GetAddr()
{
return &_lock;
}
~Mutex()
{
pthread_mutex_destroy(&_lock);
}
private:
pthread_mutex_t _lock;
};
#endif
效果:

总结:
-
利用信号量
sem_wait和sem_post控制环形队列的读写位置。 -
生产者将
Task()封装为std::function推入队列,消费者并发争抢执行。 -
利用条件变量/信号量保证队列满时阻塞生产、队列空时阻塞消费,保护临界资源。