可以完成线程间与进程间的同步与互斥
资源计数器 + PCB等待队列 + 提供等待和唤醒的接口
在条件变量中,我们了解到条件变量是实现线程间同步功能的一种方式。而posix信号量和条件变量相比,多了一个资源计数器。
资源计数器的作用就是用来对临界资源进行比较,posix信号量通过判断自身的资源计数器的情况,来得到当前资源是否可用的信息:
sem_t
eg:sem_t sem;
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
当使用sem_init()
函数初始化信号量为进程间的时候,会在共享内存中开辟一段共享内存,用来保存信号量的数据结构,其中包括资源计数器和PCB等待队列。所以调用唤醒或者等待接口的本质就是通过操作共享内存实现了不同进程之间的通信,进而实现不同进程间的同步与互斥。
而在线程之间,一般就把这个信号量设置为全局变量或者类的私有成员变量,就可以通过操作这个变量来实现不同线程之间的同步和互斥。
#include
//阻塞等待
int sem_wait(sem_t *sem);
//非阻塞等待
int sem_trywait(sem_t *sem);
//带有时间的等待
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
#include
int sem_post(sem_t *sem);
#include
int sem_destroy(sem_t *sem);
一个用数组模拟的环形队列
这个队列需要满足先进先出的特性
(当前位置 + 1) % 数组容量
生产:
sem_init(&_SemProducer,0,数组容量);//初始化
sem_wait(&_SemProducer);//如果队列满了,资源没有消耗
arr[] = ?;//生产资源
sem_post(&_SemConsumer);//通知消费
消费:
sem_init(&_SemConsumer,0,0);//初始化
sem_wait(&_SemConsumer);//如果队列为空,没有资源
*Data = arr[];//消费资源
sem_post(&_SemProducerr);//通知生产
sem_init(&_Lock,0,1);
只设置一个临界资源,让临界资源同时只能被一个线程访问。
class LoopQueue
{
public:
LoopQueue()
: _arr(SIZE)
{
_Producer = 0;
_Consumer = 0;
_Capacity = SIZE;
//同步生产者,信号量计数器的值和数组的容量一样大
sem_init(&_SemProducter,0,_Capacity);
//同步消费者,一开始并没有一个有效元素,所以初始化为0,
sem_init(&_SemConsumer,0,0);
//互斥,初始化资源数为1,只能有一个线程同时访问资源
sem_init(&_Lock,0,1);
}
//操作生产者
void Push(int& Data)
{
sem_wait(&_SemProducter);//获取当前信号量到生产者这里,如果拿不到就阻塞等待
//到这里说明,生产者需要生产资源了
sem_wait(&_Lock);
_arr[_Producer] = Data;
_Producer = (_Producer + 1) % _Capacity;
sem_post(&_Lock);
sem_post(&_SemConsumer);//通知消费者消费
}
//操作消费者
void Pop(int* Data)
{
sem_wait(&_SemConsumer);//获取当前信号量到消费者这里,如果拿不到就阻塞等待
//到这里说明,消费者可以消费资源了
sem_wait(&_Lock);
*Data = _arr[_Consumer];
_Consumer = (_Consumer + 1) % _Capacity;
sem_post(&_Lock);
sem_post(&_SemProducter);//消费完毕,通知生产者生产
}
~LoopQueue()
{
sem_destroy(&_SemConsumer);
sem_destroy(&_SemProducter);
sem_destroy(&_Lock);
}
private:
vector<int> _arr;
size_t _Capacity;
// Double pointer
int _Consumer;
int _Producer;
//同步
sem_t _SemProducter;
sem_t _SemConsumer;
//互斥
sem_t _Lock;
};
void* Consumer_start(void* arg)
{
LoopQueue* que = (LoopQueue*) arg;
int Data;
while(1)
{
que->Pop(&Data);
printf("_Consumer [%p]--> [%d]\n",pthread_self(),Data);
}
return NULL;
}
void* Producer_start(void* arg)
{
LoopQueue* que = (LoopQueue*) arg;
int Data = 1;
while(1)
{
que->Push(Data);
printf("_Producer [%p] --> [%d]\n",pthread_self(),Data);
Data++;
}
return NULL;
}
int main()
{
LoopQueue* que = new LoopQueue();
pthread_t com_tid[THREADCOUNT],pro_tid[THREADCOUNT];
for(int i = 0; i < THREADCOUNT; i++)
{
int ret = pthread_create(&com_tid[i],NULL,Consumer_start,(void*)que);
if(ret < 0)
{
perror("pthread_create consumer");
return 0;
}
ret = pthread_create(&pro_tid[i],NULL,Producer_start,(void*)que);
if(ret < 0)
{
perror("pthread_create Producter");
return 0;
}
}
for(int i = 0; i < THREADCOUNT; i++)
{
pthread_join(com_tid[i],NULL);
pthread_join(pro_tid[i],NULL);
}
delete que;
que = NULL;
return 0;
}
https://github.com/duchenlong/linux-text/blob/master/thread/semqueue.cpp