Linux-(信号量)银行-客人问题

1个窗口,10个座位,20个客人

Posted by MetaNetworks on October 14, 2019
本页面总访问量

背景

某银行提供1个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务。

关键点

  • 信号量

    PS1:服务窗口只有1个,所以不需要设置信号量

    PS2:macOS不支持匿名信号量,不支持使用sem_init而改用sem_open

    PS3:使用完使用Ctrl+C终止(csignal支持捕获按键)后,也需要清除匿名信号量,使用兼容性好的sem_unlink

    • 取完号等待的人数
    • 取号机子是否在使用(0,1互斥)
    • 座位剩余数
  • pthread多线程

代码实现(C++)

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
/*
 * @Author: MetaNetworks
 * @Date: 2019-10-14 16:53:28
 * @LastEditors: MetaNetworks
 * @LastEditTime: 2019-10-14 23:06:45
 * @Description: MetaNetworks' Code
 */

// 某银行提供1个服务窗口和10个供顾客等待的座位。
// 顾客到达银行时,若有空座位,则到取号机上领取一个号,
// 等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,
// 通过叫号选取一位顾客,并为其服务。顾客和营业员的活动过程描述如下:

#include <iostream>
#include <stdlib.h>
#include <unistd.h>
// MultiProcess
#include <pthread.h>
// Semaphore
#include <semaphore.h>
// Register Signal
#include <csignal>

//definition
#define CUSTOMERNUM 20
#define SEATSNUM 10

// 座位数
sem_t* seats;
// 取完号等待的人数
sem_t* wait_people;
// 互斥信息量,机子
sem_t* machine;
// 行为函数
void * customer(void *);
void * window(void *);
void sig_handler(int sig);
// 常量
int servedPeople = 0;
// 主函数
int main(int argc, char const *argv[]){
    // 注册 Register
    signal(SIGINT,sig_handler);
    // 20 customers
    pthread_t customers[CUSTOMERNUM];
    // 1 window
    pthread_t windows;
    // initail 
    seats = sem_open("/seats",O_CREAT,0644,SEATSNUM);
    // sem_mutex
    machine = sem_open("/machine",O_CREAT,0644,1);
    // waiting row
    wait_people = sem_open("/waitPeople",O_CREAT,0644,0);
    // bank
    pthread_create(&windows,NULL,&window,NULL);
    // customer
    for (int i = 0; i < CUSTOMERNUM; i++)
    {
        pthread_create(&customers[i],NULL,&customer,NULL);
    }
    // wait for sub process
    for (int i = 0; i < CUSTOMERNUM; i++)
    {
        pthread_join(customers[i],NULL);
    }
    pthread_join(windows,NULL);
    return 0;
}

// 按下Ctrl+C后需要销毁
void sig_handler(int sig){
    if (sig == SIGINT)
    {
        // destory semephrone
        printf("\n销毁信号量\n");
        sem_unlink("/seats");
        sem_unlink("/machine");
        sem_unlink("/waitPeople");
        exit(0);
    }
}

// show message in terminal
void * showMessage(char* words){
    printf("%s\n",words);
    return NULL;
}

void * customer(void *){
    //showMessage("我是一名顾客");
    sem_wait(seats);
    sem_wait(machine);
    // 取号
    printf("客人:已取号\n");
    sem_post(machine);
    // 开始等待,通知有等待的人数
    sem_post(wait_people);
    // 起身,释放座位,接收服务
    sem_post(seats);
    printf("客人:离开\n");
    return NULL;
}

void * window(void *){
    //showMessage("我是一个窗口");
    while (true)
    {
        sem_wait(wait_people);
        // 服务
        printf("银行:叫号\n");
        // 叫号
        servedPeople += 1;
        printf("银行:已经为%d号顾客服务\n",servedPeople);
    }
    return NULL;
}

执行效果

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
客人:已取号
客人:已取号
客人:已取号
客人:离开
银行:叫号
客人:已取号
银行:已经为1号顾客服务
银行:叫号
银行:已经为2号顾客服务
银行:叫号
银行:已经为3号顾客服务
银行:叫号
银行:已经为4号顾客服务
客人:离开
客人:离开
客人:离开
客人:已取号
客人:离开
银行:叫号
客人:已取号
银行:已经为5号顾客服务
客人:已取号
客人:离开
银行:叫号
银行:已经为6号顾客服务
银行:叫号
银行:已经为7号顾客服务
客人:离开
客人:已取号
客人:已取号
银行:叫号
客人:离开
银行:已经为8号顾客服务
银行:叫号
银行:已经为9号顾客服务
客人:离开
客人:已取号
客人:离开
银行:叫号
银行:已经为10号顾客服务
客人:已取号
客人:离开
银行:叫号
客人:已取号
银行:已经为11号顾客服务
客人:离开
银行:叫号
银行:已经为12号顾客服务
客人:已取号
客人:离开
客人:已取号
银行:叫号
银行:已经为13号顾客服务
客人:已取号
客人:离开
银行:叫号
银行:已经为14号顾客服务
客人:已取号
客人:离开
客人:离开
银行:叫号
客人:已取号
银行:已经为15号顾客服务
银行:叫号
银行:已经为16号顾客服务
客人:离开
客人:已取号
银行:叫号
客人:已取号
客人:离开
客人:离开
银行:已经为17号顾客服务
银行:叫号
银行:已经为18号顾客服务
银行:叫号
银行:已经为19号顾客服务
客人:已取号
客人:离开
银行:叫号
银行:已经为20号顾客服务
^C
销毁信号量