信号量
进化版的互斥锁(1 --> N)
由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。线程从并行执行,变成了串行执行。与直接使用单进程无异。
信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发
主要应用函数:
sem_init 函数
sem_destroy 函数
sem_wait 函数
sem_trywait 函数
sem_timedwait 函数
sem_post 函数
以上 6 个函数的返回值都是:成功返回 0, 失败返回-1,同时设置 errno。(注意,它们没有 pthread 前缀)
sem_t 类型本质仍是结构体。但应用期间可简单看作为整数,忽略实现细节(类似于使用文件描述符)。
sem_t sem; 规定信号量 sem 不能 < 0。头文件 <semaphore.h>
信号量基本操作:
sem_wait:
- 信号量大于 0,则信号量-- (类比 pthread_mutex_lock)
- 信号量等于 0,造成线程阻塞
对应
sem_post:
将信号量++,同时唤醒阻塞在信号量上的线程 (类比 pthread_mutex_unlock)但由于 sem_t 的实现对用户隐藏,所以所谓的++、–操作只能通过函数来实现,而不能直接++、–符号。
信号量的初值,决定了占用信号量的线程的个数。
sem_init 函数: 初始化一个信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参 1:sem 信号量
参 2:pshared 取 0 用于线程间;取非 0(一般为 1)用于进程间
参 3:value 指定信号量初值
sem_destroy 函数:
销毁一个信号量
int sem_destroy(sem_t *sem);
sem_wait 函数:
给信号量加锁 –
int sem_wait(sem_t *sem);
sem_post 函数:
给信号量解锁 ++
int sem_post(sem_t *sem);
例:生产者消费者信号量模型
规定: 如果数组中有数据,生产者不能生产,只能阻塞。
如果数组中没有数据,消费者不能消费,只能等待数据。
代码
#include <iostream>
2 #include <pthread.h>
3 #include <fcntl.h>
4 #include <string>
5 #include <string.h>
6 #include <semaphore.h>
7
8 using namespace std;
9
10 int deque[5];
11
12 sem_t black_number, peoduct_number;
13
14 void err_pthread(int ret, string str)
15 {
16 if(ret != 0)
17 {
18 cout<<str<<endl;
19 cout<<strerror(ret)<<endl;
20 }
21 }
22
23 void *produce(void *arg)
24 {
25 int i = 0;
26 while(1)
27 {
28 sem_wait(&black_number);
29 deque[i] = rand()%100+1;
cout<<"produce : "<<deque[i]<<endl;
31
32 sem_post(&peoduct_number);
33
34 i = (i + 1)%5;
35
36 sleep(rand()%1);
37 }
38
39 return NULL;
40 }
41
42 void *consumer(void *arg)
43 {
44 int i = 0;
45 while(1)
46 {
47 sem_wait(&peoduct_number);
48 cout<<"--------consumer : "<<deque[i]<<endl;
49 deque[i] = 0;
50 sem_post(&black_number);
51
52 i = (i + 1)%5;
53
54 sleep(rand()%3);
55 }
56
57 return NULL;
58 }
59 int main()
60 {
61 pthread_t pid, cid;
62 srand(time(NULL));
63 sem_init(&black_number, 0, 5);
64 sem_init(&peoduct_number, 0, 0);
66 int ret = pthread_create(&pid, NULL, produce, NULL);
67 err_pthread(ret, "pthread error");
68
69 ret = pthread_create(&cid, NULL, consumer, NULL);
70 err_pthread(ret, "pthread error");
71
72 ret = pthread_join(pid, NULL);
73 err_pthread(ret, "pthread_join error");
74
75 ret = pthread_join(cid, NULL);
76 err_pthread(ret, "pthread_join error");
77
78 sem_destroy(&black_number);
79 sem_destroy(&peoduct_number);
80
81 return 0;
82 }
参考资源
https://www.bilibili.com/video/BV1iJ411S7UA/?p=2&spm_id_from=pageDriver