今天来讲下POSIX信号量,用于同步操作,达到无冲突的访问共享资源的目的,可以用于线程间同步。
信号量就是一个计数器+等待队列+等待+唤醒
1.信号量的基本接口
1)初始化信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem:信号量变量
pshared:0表示线程间共享,非0表示进程间共享(决定用于线程间还是进程间)
value:信号量初始值
2)销毁信号量
int sem_destroy(sem_t *sem);
3)等待信号量
功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t *sem);
4)发布信号量
功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。 int sem_post(sem_t *sem);
信号量和条件变量的区别:
信号量与条件变量的区别:信号量拥有资源计数的功能,临界资源是否能够操作通过自身技术判断,条件变量是搭配互斥锁一起使用的,信号量还可以实现互斥,计数仅为0/1< font>
2.信号量实现生产者与消费者模型
上一节我用基本队列实现了一个生产者与消费者模型,这节我将用信号量和大小固定的环形
队列再次实现生产者与消费者模型:
实现代码
1 #include<iostream>
2 #include<pthread.h>
3 #include<vector>
4 #include<semaphore.h>
5
6 //实现线程安全的环形队列,用信号量和线性表实现
7 //信号量就是一个计数器,具有等待队列+等待+唤醒功能
8
9 class RingQueue
10 {
11 public:
12 RingQueue(int cap = 10):queue(10),capacity(cap) //构造函数,采用了初始化列表的形式,定义环形队列大小capacity为10
13 ,write_step(0),read_step(0){
14 //int sem_init(sem_t *sem, int pshared, unsigned int value);
15 //sem:信号量变量
16 //pshared:决定信号量用于线程还是进程
17 //0为线程 !0为进程
18 //value:信号量初始计数
19 sem_init(&data,0,0);
20 sem_init(&idle,0,cap);
21 sem_init(&lock,0,1);
22 }
23 ~RingQueue(){ //析构函数
24 sem_destroy(&data);
25 sem_destroy(&idle);
26 sem_destroy(&lock);
27 }
28 bool QueuePush(int data){
29 ProductorWait(); //因为信号量自带一个计数功能,所以不需要在循环判断,先加一个等待是为了判断是否队列空了,如果先加锁然后却发现满了程序就会卡在productorwait()那里没有解锁。
30 QueueLock(); //访问临界资源加锁
31 queue[write_step] = data; //放数据
32 write_step = ((write_step)+1)%capacity; //写指针要改变
33 QueueUnlock(); //访问完毕后解锁,因为锁只是限制临界资源的访问,不限制其他操作
34 ConsumorWakeUp(); //唤醒消费者
35 return true;
36 }
37 bool QueuePop(int *data){
38 ConsumorWait(); //同理先加一个等待是为了判断是否有资源可以取,否则先加锁了,结果发现没有临界资源可以访问,则会卡在consumorwait()那里没有解锁。
39 QueueLock();
40 *data = queue[read_step]; //取数据
41 read_step = ((read_step)+1)%capacity; //渎指针要改变
42 QueueUnlock();
43 ProductorWakeUp();
44 return true;
45 }
46 private:
47 void QueueLock(){
48 sem_wait(&lock);
49 }
50 void QueueUnlock(){
51 sem_post(&lock);
52 }
53 void ConsumorWait(){
54 sem_wait(&data);
55 }
56 void ConsumorWakeUp(){
57 sem_post(&data);
58 }
59 void ProductorWait(){
60 sem_wait(&idle);
61 }
62 void ProductorWakeUp(){
63 sem_post(&idle);
64 }
65 private:
66 std::vector<int> queue;
67 int capacity;
68 int write_step;
69 int read_step;
70 sem_t data; //代表消费者信号量
71 sem_t idle; //代表生产者信号量
72 sem_t lock;
73 };
74
75 void* pth_consumor(void* arg)
76 {
77 RingQueue *q = (RingQueue*) arg;
78 while(1){
79 int data;
80 q->QueuePop(&data);
81 std::cout<<"consumor get data!"<<std::endl;
82 }
83 return NULL;
84 }
85
86 void* pth_productor(void* arg)
87 {
88 RingQueue* q = (RingQueue*) arg;
89 int i=0;
90 while(1){
91 q->QueuePush(i);
92 std::cout<<"productor put data!"<<std::endl;
93 i++;
94 }
95 return NULL;
96 }
97
98 int main()
99 {
100 pthread_t ctid[4],ptid[4];
101 int ret,i;
102 RingQueue q;
103 for(i=0;i<4;i++){
104 ret = pthread_create(&ctid[i],NULL,pth_consumor,(void*)&q);
105 if(ret!=0){
106 std::cout<<"consumor not exist!"<<std::endl;
107 return -1;
108 }
109 }
110 for(i=0;i<4;i++){
111 ret = pthread_create(&ptid[i],NULL,pth_productor,(void*)&q);
112 if(ret!=0){
113 std::cout<<"productor not exist!"<<std::endl;
114 return -1;
115 }
116 }
117 for(i=0;i<4;i++){ //线程等待
118 pthread_join(ctid[i],NULL);
119 }
120 for(i=0;i<4;i++){
121 pthread_join(ptid[i],NULL);
122 }
123 return 0;
124 }