银行窗口服务 —— 生产者与消费者 —— 线程同步实例

本文最后更新于:2021年4月24日 晚上

概览:生产者与消费者 —— 线程同步实例,信号量与条件变量

生产者与消费者 —— 线程同步实例

2021/04/23 招银网络科技一面

场景:银行

2个柜台窗口,服务顾客

10个空闲座位,供还未办理服务的顾客暂坐,只有通过预约机拿到号码的顾客才能坐。

1个预约机,顾客通过预约机器拿到对应号码。

1个叫号机,柜台人员通过叫号机才能呼叫顾客前往。


使用多线程模拟这个问题,写出程序。

  1. 行人互斥的访问预约机器,若队列不满时,可以申请到号,做到对应座位。(生产者)
  2. 2个柜台窗口互斥的访问叫号机,若队列不空的时候,可以叫到顾客。(消费者)

信号量与锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>


int ticket = 1;//号码开始位置

pthread_mutex_t booked; //预约机器
pthread_mutex_t called; //叫号机器

sem_t sites; //信号量,10个位置
sem_t person; //信号量,待服务人数


//队列
typedef struct ListNode{
int val;
struct ListNode * next;
}Node;

struct ListNode* head;//头节点,指向头部的节点
struct ListNode* tail;//尾节点


//行人线程,不断预约,申请号码
void* passerby(void * arg){
srand((unsigned)time( NULL ));
while(1){

sem_wait(&sites);//申请一个位置

pthread_mutex_lock(&booked);//加锁

//创建新节点
Node* newNode = (Node*)malloc(sizeof(Node));

int val = 0;
sem_getvalue(&sites,&val);

printf("%ld 顾客正在叫号:%d, 剩余位置:%d\n",pthread_self(),ticket,val);

newNode->val = ticket++;
newNode->next = NULL;
tail->next = newNode;
tail = newNode;

pthread_mutex_unlock(&booked);

sem_post(&person);
int waitnum = rand()% 10;
sleep(waitnum);
}
return NULL;
}

//服务线程,不断叫号,处理
void* server(void* arg){
srand((unsigned)time( NULL ));
while(1){

sem_wait(&person);//叫一个人

pthread_mutex_lock(&called);//加锁

//消耗一个节点
Node* tmp = head->next;
if(tmp == tail){
tail = head;
}

int val = 0;
sem_getvalue(&person,&val);

printf("%ld 正在服务 %d 号顾客, 剩余顾客%d \n",pthread_self(),tmp->val,val);
head->next = tmp->next;
free(tmp);

pthread_mutex_unlock(&called);//解锁

sem_post(&sites);

int sleeptime = rand()%20;
sleep(sleeptime);
}

return NULL;
}

int main(){

//信号量、互斥量初始化
pthread_mutex_init(&called,NULL);
pthread_mutex_init(&booked,NULL);
sem_init(&sites,0,10);
sem_init(&person,0,0);

//链表初始化
head = (Node *)malloc(sizeof(Node));
head->next = NULL;
tail = head;

//两个服务窗口,2个地方不断地来人预约
pthread_t sertid[2],pastid[2];
for(int i=0;i<2;i++){
pthread_create(&sertid[i],NULL,server,NULL);
pthread_create(&pastid[i],NULL,passerby,NULL);
}

//回收线程
for(int i=0;i<2;i++){
pthread_join(sertid[i],NULL);
pthread_join(pastid[i],NULL);
}

pthread_mutex_destroy(&booked);
pthread_mutex_destroy(&called);

pthread_exit(NULL);

return 0;
}

执行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
140260868921088 顾客正在叫号:1, 剩余位置:9
140260852135680 顾客正在叫号:2, 剩余位置:8
140260877313792 正在服务 1 号顾客, 剩余顾客0
140260852135680 顾客正在叫号:3, 剩余位置:7
140260860528384 正在服务 2 号顾客, 剩余顾客1
140260868921088 顾客正在叫号:4, 剩余位置:8
140260852135680 顾客正在叫号:5, 剩余位置:7
140260860528384 正在服务 3 号顾客, 剩余顾客2
140260852135680 顾客正在叫号:6, 剩余位置:7
140260852135680 顾客正在叫号:7, 剩余位置:6
140260868921088 顾客正在叫号:8, 剩余位置:5
140260877313792 正在服务 4 号顾客, 剩余顾客4
140260868921088 顾客正在叫号:9, 剩余位置:5
140260860528384 正在服务 5 号顾客, 剩余顾客4
140260852135680 顾客正在叫号:10, 剩余位置:5
140260860528384 正在服务 6 号顾客, 剩余顾客4
140260868921088 顾客正在叫号:11, 剩余位置:5
140260852135680 顾客正在叫号:12, 剩余位置:4
140260877313792 正在服务 7 号顾客, 剩余顾客5
140260868921088 顾客正在叫号:13, 剩余位置:4
140260852135680 顾客正在叫号:14, 剩余位置:3
140260860528384 正在服务 8 号顾客, 剩余顾客5

条件变量与锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <time.h>


int ticket = 1;

pthread_mutex_t called;
pthread_mutex_t booked;

pthread_cond_t cond; //条件变量

//队列
typedef struct ListNode{
int val;
struct ListNode * next;
}Node;

struct ListNode* head;//头节点,指向头部的节点
struct ListNode* tail;//尾节点

int sitenum;
int personnum;

//行人线程,不断预约,申请号码
void* passerby(void * arg){
srand((unsigned)time( NULL ));
while(1){

pthread_mutex_lock(&booked);//加锁

//创建新节点
Node* newNode = (Node*)malloc(sizeof(Node));
printf("顾客正在叫号:%d, 剩余位置:%d\n",ticket,--sitenum);
newNode->val = ticket++;
newNode->next = NULL;
tail->next = newNode;
tail = newNode;
personnum++;

pthread_cond_signal(&cond);//通知消费者

pthread_mutex_unlock(&booked);

int waitnum = rand()% 10;
sleep(waitnum);
}
return NULL;
}

//服务线程,不断叫号,处理
void* server(void* arg){
srand((unsigned)time( NULL ));
while(1){

pthread_mutex_lock(&called);//加锁

//消耗一个节点
if(head->next != NULL){
Node* tmp = head->next;
if(tmp == tail){
tail = head;
}
printf("%ld 正在服务 %d 号顾客, 剩余顾客%d \n",pthread_self(),tmp->val,--personnum);
head->next = tmp->next;
free(tmp);
sitenum++;

pthread_mutex_unlock(&called);//解锁

int sleeptime = rand()%20;
sleep(sleeptime);
}else{
//等待,进入阻塞,会解锁,当不阻塞地时候,又会再次加锁,然后继续向下执行
pthread_cond_wait(&cond,&called);
pthread_mutex_unlock(&called);//解锁
}
}

return NULL;
}

int main(){

//信号量、互斥量初始化
pthread_mutex_init(&called,NULL);
pthread_mutex_init(&booked,NULL);
pthread_cond_init(&cond,NULL);

//链表初始化
head = (Node *)malloc(sizeof(Node));
head->next = NULL;
tail = head;

sitenum = 10;
personnum = 0;

//两个服务窗口,2个地方不断地来人预约
pthread_t sertid[2],pastid[2];
for(int i=0;i<2;i++){
pthread_create(&sertid[i],NULL,server,NULL);
pthread_create(&pastid[i],NULL,passerby,NULL);
}

//回收线程
for(int i=0;i<2;i++){
pthread_join(sertid[i],NULL);
pthread_join(pastid[i],NULL);
}

pthread_cond_destroy(&cond);
pthread_mutex_destroy(&booked);
pthread_mutex_destroy(&called);

pthread_exit(NULL);

return 0;
}
  • 注意:只是为了显示直观,所以加了两个变量来查看剩余顾客数和座位数,这两个变量是没有锁保护的,在并发条件下可能会出问题!

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
顾客正在叫号:1, 剩余位置:9
140507854608128 正在服务 1 号顾客, 剩余顾客0
顾客正在叫号:2, 剩余位置:9
140507837822720 正在服务 2 号顾客, 剩余顾客0
顾客正在叫号:3, 剩余位置:9
140507854608128 正在服务 3 号顾客, 剩余顾客0
顾客正在叫号:4, 剩余位置:9
140507854608128 正在服务 4 号顾客, 剩余顾客0
顾客正在叫号:5, 剩余位置:9
140507837822720 正在服务 5 号顾客, 剩余顾客0
顾客正在叫号:6, 剩余位置:8
140507854608128 正在服务 6 号顾客, 剩余顾客0
顾客正在叫号:7, 剩余位置:8
顾客正在叫号:8, 剩余位置:8
顾客正在叫号:9, 剩余位置:7
顾客正在叫号:10, 剩余位置:6
顾客正在叫号:11, 剩余位置:5
140507837822720 正在服务 7 号顾客, 剩余顾客4
顾客正在叫号:12, 剩余位置:5
140507837822720 正在服务 8 号顾客, 剩余顾客4
140507837822720 正在服务 9 号顾客, 剩余顾客3
顾客正在叫号:13, 剩余位置:6

(7条消息) 【操作系统】生产者消费者问题_liushall-CSDN博客_生产者消费者问题

https://blog.csdn.net/iteye_10993/article/details/82333238?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control