实现一个银行系统该银行设置有M个窗口。每个用户需要先取号只有一台取号机。用户分为对公用户私人用户。私人用户又分为普通用户VIP用户。其中窗口的柜员在服务时按优先级给用户服务其中对公用户的优先级最高VIP用户次之普通用户优先级最低。请使用P、V操作解决此银行的叫号服务系统。
以下是一个可能的解决方案:
- 定义共享数据结构:
typedef struct {
int ticket_number; // 当前的票号
int next_customer; // 下一个客户的编号
int serving_customer; // 当前正在被服务的客户的编号
int num_windows; // 窗口数量
int *window_status; // 窗口状态数组,0表示空闲,1表示正在服务
int *priority_queue; // 优先级队列,保存客户的编号,按照优先级排序
int queue_length; // 队列长度
int queue_capacity; // 队列容量
} Bank;
- 初始化银行系统:
void init_bank(Bank *bank, int num_windows, int queue_capacity) {
bank->ticket_number = 0;
bank->next_customer = 1;
bank->serving_customer = -1;
bank->num_windows = num_windows;
bank->window_status = (int*) malloc(num_windows * sizeof(int));
for (int i = 0; i < num_windows; i++) {
bank->window_status[i] = 0; // 所有窗口空闲
}
bank->priority_queue = (int*) malloc(queue_capacity * sizeof(int));
bank->queue_length = 0;
bank->queue_capacity = queue_capacity;
}
- 实现取号操作:
int get_ticket(Bank *bank, int customer_type) {
P(&mutex); // 进入临界区
int ticket_number = bank->ticket_number + 1;
bank->ticket_number = ticket_number;
int customer_priority = 0;
if (customer_type == CUSTOMER_PUBLIC) {
customer_priority = 2;
} else if (customer_type == CUSTOMER_VIP) {
customer_priority = 1;
} else {
customer_priority = 0;
}
int insert_index = -1;
for (int i = 0; i < bank->queue_length; i++) {
int priority = 0;
if (bank->priority_queue[i] == 0) { // 对公客户
priority = 2;
} else if (bank->priority_queue[i] < 1000) { // VIP客户
priority = 1;
} else { // 普通客户
priority = 0;
}
if (customer_priority > priority) { // 找到插入位置
insert_index = i;
break;
}
}
if (insert_index == -1 && bank->queue_length < bank->queue_capacity) { // 队列未满
insert_index = bank->queue_length;
bank->queue_length++;
}
if (insert_index != -1) { // 插入队列
for (int i = bank->queue_length - 1; i > insert_index; i--) {
bank->priority_queue[i] = bank->priority_queue[i-1];
}
bank->priority_queue[insert_index] = ticket_number;
}
V(&mutex); // 离开临界区
return ticket_number;
}
- 实现客户等待操作:
void wait_for_turn(Bank *bank, int customer_number) {
while (1) {
P(&mutex); // 进入临界区
int priority = 0;
if (customer_number == 0) { // 对公客户
priority = 2;
} else if (customer_number < 1000) { // VIP客户
priority = 1;
} else { // 普通客户
priority = 0;
}
if (bank->serving_customer == -1) { // 没有正在服务的客户
for (int i = 0; i < bank->num_windows; i++) {
if (bank->window_status[i] == 0) { // 找到空闲窗口
bank->window_status[i] = 1; // 标记为正在服务
bank->serving_customer = customer_number; // 记录正在服务的客户
V(&mutex); // 离开临界区
break;
}
}
} else if (priority > 0) { // 当前客户的优先级高于正在服务客户的优先级
int found = 0;
for (int i = 0; i < bank->num_windows; i++) {
if (bank->window_status[i] == 0) { // 找到空闲窗口
bank->window_status[i] = 1; // 标记为正在服务
bank->priority_queue[0] = bank->serving_customer; // 将正在服务的客户放回队列
for (int j = 1; j < bank->queue_length; j++) { // 重新排序队列
int priority_j = 0;
if (bank->priority_queue[j] == 0) {
priority_j = 2;
} else if (bank->priority_queue[j] < 1000) {
priority_j = 1;
} else {
priority_j = 0;
}
if (priority > priority_j) {
for (int k = bank->queue_length-1; k >= j; k--) {
bank->priority_queue[k] = bank->priority_queue[k-1];
}
bank->priority_queue[j-1] = customer_number;
found = 1;
break;
}
}
if (!found && bank->queue_length < bank->queue_capacity) { // 队列未满
bank->priority_queue[bank->queue_length] = customer_number;
bank->queue_length++;
found = 1;
}
if (found) { // 找到了新客户
bank->serving_customer = customer_number; // 记录正在服务的客户
V(&mutex); // 离开临界区
break;
} else { // 没有找到新客户
bank->serving_customer = -1; // 没有正在服务的客户
V(&mutex); // 离开临界区
sleep(1); // 等待1秒再重新进入循环
break;
}
}
}
} else { // 当前客户的优先级低于正在服务客户的优先级
P(&turn); // 释放互斥锁并等待被唤醒
V(&mutex); // 离开临界区
}
}
}
- 实现客户完成操作:
void complete_transaction(Bank *bank, int customer_number) {
P(&mutex); // 进入临界区
for (int i = 0; i < bank->num_windows; i++) {
if (bank->window_status[i] == 1 && bank->serving_customer == customer_number) { // 找到正在服务的客户
bank->window_status[i] = 0; // 标记为空闲
bank->serving_customer = -1; // 没有正在服务的客户
if (bank->queue_length > 0) { // 从队列中选取下一个客户
int next_customer = bank->priority_queue[0];
for (int j = 0; j < bank->queue_length-1; j++) { // 重新排序队列
bank->priority_queue[j] = bank->priority_queue[j+1];
}
bank->queue_length--;
bank->serving_customer = next_customer; // 记录正在服务的客户
V(&turn); // 唤醒下一个客户
}
break;
}
}
V(&mutex); // 离开临界区
}
注意事项:
- 使用P、V操作前需要先定义信号量,如下所示:
sem_t mutex;
sem_t turn;
int main() {
sem_init(&mutex, 0, 1);
sem_init(&turn, 0, 0);
...
}
- P操作和V操作分别是sem_wait和sem_post函数,使用时需要注意线程安全性
原文地址: https://www.cveoy.top/t/topic/eyQc 著作权归作者所有。请勿转载和采集!