C++实现银行排队系统

网上看到的设计要求:

基本效果已经实现,希望大家帮忙指点指点。

程序中的一些基本模块

程序处理中的一些流程图

程序运行结果如下图:

程序代码如下:

#include <iostream>
#include <string>
#include <random>
#include <ctime>
#include <cassert>
#include <vector>
#include <queue>
#include <thread>
#include <atomic>
#include <chrono>
#include <sstream>
#include <iomanip>
#include <memory>
#include <mutex>


#define DESTIME 100

#define LOG(x)  std::cout << #x << std::endl



int getRandomNum(int lnum, int rnum)
{
	// 设定随机数种子,使用当前时间
	std::random_device rd;
	std::mt19937 gen(rd());

	// 定义随机数分布范围,这里使用均匀分布
	std::uniform_int_distribution<> dis(lnum, rnum); // 例如,生成1到100之间的随机数

	// 生成随机数
	int random_number = dis(gen);

	return random_number;
}


//HHMM时间添加int时间,返回HHMM格式时间
std::string addMinutesToTime(const std::string& timeStr, int minutesToAdd) {
	int hours, minutes;
	char delimiter;

	//解析输入时间
	std::istringstream iss(timeStr);

	if (!(iss >> std::setw(2) >> hours >> delimiter >> std::setw(2) >> minutes) || delimiter != '-')
	{
		throw std::invalid_argument("错误的时间格式,请输入HH-MM格式");
	}


	//将时间间隔加到分钟上
	minutes += minutesToAdd;

	hours += minutes / 60;

	hours %= 24;		//24小时取余
	minutes %= 60;	    //60分钟取余


	//格式化输出时间
	std::ostringstream oss;

	oss << std::setw(2) << std::setfill('0') << hours << "-"
		<< std::setw(2) << std::setfill('0') << minutes;

	return oss.str();
}

//HHMM 格式时间数据转换为int类型数据
int numToHHMMtime(const std::string strTime)
{
	int timeNum = 0;
	char delimiter;

	int hours, minutes;

	std::istringstream iss(strTime);

	if (!(iss >> std::setw(2) >> hours >> delimiter >> std::setw(2) >> minutes) || delimiter != '-' || hours >= 24 || hours < 0 || minutes < 0 || minutes >= 60)
	{
		throw std::invalid_argument("错误的时间,请输入正确的HH-MM格式数据");
	}

	minutes += (hours * 60);

	return minutes;
}


//客户类
class Customer {
public:

	//设置VIP
	void setVIP(bool vip);

	//设置进入时间
	void setEnterTime(const std::string time);

	//设置离开时间
	void setLeftTime(const std::string time);

	//设置排队号码
	void setQueueNumber(unsigned int num);

	//生成评分(传入满分,随机生成区间内分数)
	int getScore(int score);

	//判断是否是VIP
	bool isVIP() {
		return VIP;
	}

	//获取进入时间
	std::string getEnterTime() {
		return enterTime;
	}

	//获取离开时间
	std::string getLeftTime() {
		return leftTime;
	}

	//获取排队号码
	unsigned int getQueueNumber() {
		return queueNumber;
	}

private:
	bool VIP = false;					//VIP标志
	std::string enterTime = " ";		//进入时间
	std::string leftTime = " ";			//离开时间
	unsigned int queueNumber = 0;		//排队号码
};


//评分类
class Rating {
public:
	//设置分数
	void setScore(int _num);

	//设置评分时间
	void setRatingTime(const std::string time);

	//获取分数
	int getScore() {
		return score;
	}

	//获取评分时间
	std::string getRatingTime() {
		return ratingTime;
	}

private:
	std::string ratingTime = " ";		//评分时间
	int score = 0;				    //评分分数
};


//窗口类
class CounterWindow {
public:

	CounterWindow(int num) :windowNum(num) {};

	void setVIP() {
		VIPflag = true;
	}

	//添加客户
	void addCustomer(Customer& customer);

	//移除客户(这里不考虑客户不排队了的情况)
	void removeCustomer() {

		//先移除VIP还是先移除普通用户。查询当前正在服务的VIP标志
		if (VipWorkFlag)
		{
			vipCustomerQue.pop();
		}
		else {
			customerQue.pop();
		}
	}

	//添加打分
	void addGrade();

	//办理业务
	void conductBusiness();

	//VIP队列人数
	int getVipCustomQuenNum()
	{
		return vipCustomerQue.size();
	}

	//客户队列人数
	int getCustomeQuenNum()
	{
		return customerQue.size();
	}

	//设置处理业务耗时
	void setWorkTime(int workTime)
	{
		windowTime = workTime;			    //设置窗口时间和工作时间相同
	}

	//停止业务处理
	void closeConduct() {					//关闭业务
		closeFlag = true;
	}

	//开始业务处理
	void openConduct() {					//开启业务
		closeFlag = false;
	}


private:
	//生成业务办理耗时
	void businessProcessTime();

private:
	bool VIPflag = false;					//VIP窗口标志
	bool VipWorkFlag = false;				//VIP正在工作标识符
	bool closeFlag = false;					//停止营业标志

	int windowNum;							//窗口号

	std::queue<Customer> customerQue;		//客户队列
	std::queue<Customer> vipCustomerQue;	//VIP客户队列
	std::vector<Rating> ratingVec;			//分数容器

	int windowTime = 0;						//窗口时间
};




class sysTime {
public:

	sysTime(const sysTime&) = delete;
	sysTime& operator = (const sysTime&) = delete;


	// 获取类的唯一实例
	static sysTime& getInstance() {
		static std::mutex mtx; // 用于线程安全的互斥锁
		std::lock_guard<std::mutex> lock(mtx); // 自动管理锁的获取和释放
		static sysTime instance; // 局部静态变量,线程安全地在第一次调用时初始化
		return instance;
	}

	

	//获取字符串格式时间
	std::string get_HHMM_time();

	//获取时间
	int getTime() {
		return timeNum;
	}


private:

	sysTime() : workThread(&sysTime::timeLoop, this) {
		//检查是否启动成功
		if (!workThread.joinable()) {
			std::cerr << "线程启动失败" << std::endl;
		}

		std::cout << "时间线启动成功" << std::endl;
	}

	~sysTime() {
		end();
		if (workThread.joinable()) {
			workThread.join();
		}
	}



	void start() {
		timeflag.store(false);
	}

	void end() {
		timeflag.store(true);
	}

	//循环计时
	void timeLoop();


private:
	std::atomic<bool> timeflag;
	std::thread workThread;				//时间计数工作线程
	int timeNum = 0;					//时间计数器
};


//银行类
class Bank {
public:
	//设置营业开始时间
	void setOpenTime(std::string time);

	//设置营业结束时间
	void setCloseTime(std::string time);

	//设置窗口数量
	void setNumWindow(int num);

	//进入客户
	void enterCustomer(Customer& customer);


	//打开所有线程
	void openAllThread();

	//等待关闭所有线程
	void joinAllThreads();

private:
	CounterWindow* getShortWindow(bool isVIP);

	


private:
	int businessOpenTime;							//营业开始时间
	int businessCloseTime; 							//营业结束时间

	std::vector<CounterWindow> counterWinVec;		//窗口维护容器
	std::vector<CounterWindow> counterWinVipVec;	//VIP窗口维护器


	bool openFlag = false;							//营业标志

	std::vector<std::shared_ptr<std::thread>> threads;		//线程组

};

  
class Menu {
public:

	void start();

private:

};



int main()
{
	Menu menu;
	menu.start();

	return 0;
}

void Customer::setVIP(bool vip)
{
	this->VIP = vip;
}

void Customer::setEnterTime(const std::string time)
{
	this->enterTime = time;
}

void Customer::setLeftTime(const std::string time)
{
	this->leftTime = time;
}

void Customer::setQueueNumber(unsigned int num)
{
	this->queueNumber = num;
}

int Customer::getScore(int score)
{
	//使用断言判断输入分数是否在区间内
	assert(score >= 1);

	// 设定随机数种子,使用当前时间
	std::random_device rd;
	std::mt19937 gen(rd());

	// 定义随机数分布范围,这里使用均匀分布
	std::uniform_int_distribution<> dis(1, score); // 例如,生成1到100之间的随机数

	// 生成随机数
	int random_number = dis(gen);

	return random_number;
}

void Rating::setScore(int _num)
{
	this->score = _num;
}

void Rating::setRatingTime(const std::string time)
{
	this->ratingTime = time;
}


//添加客户
void CounterWindow::addCustomer(Customer& customer) {
	if (customer.isVIP())
	{
		vipCustomerQue.push(customer);
	}
	else {
		customerQue.push(customer);
	}
}

void CounterWindow::addGrade()
{
	Rating rate;

	Customer customer;
	int scoreNum;		//分数

	if (VipWorkFlag)
	{
		customer = vipCustomerQue.front();
	}
	else {
		customer = customerQue.front();
	}

	scoreNum = customer.getScore(10);			//10分满分进行评分

	rate.setScore(scoreNum);
	rate.setRatingTime(addMinutesToTime(sysTime::getInstance().get_HHMM_time(), windowTime));

	ratingVec.push_back(rate);
	
	int personNum = customerQue.size() + vipCustomerQue.size();

	std::cout << "评分是" << rate.getScore() << " 评分时间是" << rate.getRatingTime()  << " 窗口是" << windowNum << "排队人数是" << personNum <<std::endl;

}

void CounterWindow::conductBusiness()
{
	while (!closeFlag)
	{
		int vipSize = vipCustomerQue.size();
		if (vipSize > 0) {						//判断当前窗口是否有VIP客户正在排队
			VipWorkFlag = true;				    //标志给VIP客户办理业务
		}
		else if (customerQue.size() > 0) {
			VipWorkFlag = false;
		}
		else {
			//两种条件都不满足,程序跳出
			continue;
		}

		//办理业务,这里使用线程睡眠一段时间模拟
		businessProcessTime();
		//客户评分,生成客户评分
		addGrade();
		//移除客户,移除队列里的客户
		removeCustomer();
	}
}

void CounterWindow::businessProcessTime()
{
	int num = getRandomNum(5, 20);
	
	windowTime = num;

	setWorkTime(num);
	
	num = num * DESTIME;

	

	std::chrono::milliseconds desTime(num);
	std::this_thread::sleep_for(desTime);

}

//获取字符串格式时间
std::string sysTime::get_HHMM_time()
{
	int hours, minutes;

	//输入时间余多少分钟
	minutes = timeNum % 60;

	//输入时间换算为多少小时
	hours = timeNum / 60;
	hours %= 24;

	std::ostringstream oss;

	oss << std::setw(2) << std::setfill('0') << hours << "-"
		<< std::setw(2) << std::setfill('0') << minutes;

	return oss.str();
}

void sysTime::timeLoop()
{
	start();
	while (!timeflag.load())
	{
		//使用chrono库定义时间间隔,这里使用100ms对应1s
		std::chrono::milliseconds desTime(DESTIME);
		std::this_thread::sleep_for(desTime);
		timeNum++;
	}
}

void Menu::start()
{
	// 使用单例
	sysTime& myTime = sysTime::getInstance();

	Bank bank;
	bank.setOpenTime("09-00");
	bank.setCloseTime("17-00");
	bank.setNumWindow(4);

	while (1)
	{
		int num = getRandomNum(1, 10);		//1分钟到60分钟内进入一位顾客
		num = num * DESTIME;				//得到转换后的毫秒数
		std::chrono::milliseconds sleepTime(num);
		std::this_thread::sleep_for(sleepTime);

		if (myTime.getTime() >= 1440) {		//一天时间为1440分钟
			break;
		}

		Customer customer;

		bank.enterCustomer(customer);
		std::string strTime = myTime.get_HHMM_time();
	}
	
	bank.joinAllThreads();

}

void Bank::setOpenTime(std::string time)
{
	businessOpenTime = numToHHMMtime(time);
}

void Bank::setCloseTime(std::string time)
{
	businessCloseTime = numToHHMMtime(time);
}

void Bank::setNumWindow(int num)
{
	assert(num >= 2);

	//设置一个VIP
	CounterWindow cWindow(-1);
	cWindow.setVIP();

	counterWinVipVec.push_back(cWindow);

	for (int i = 0; i < num - 1; i++)
	{
		CounterWindow window(i);
		counterWinVec.push_back(window);
	}
}


void Bank::enterCustomer(Customer& customer)
{
	int nowTime = sysTime::getInstance().getTime();


	if (nowTime < businessOpenTime || nowTime > businessCloseTime)
	{
		std::cout << "当前不在营业时间当前时间是" << sysTime::getInstance().get_HHMM_time() << std::endl;
		openFlag = false;

		if (openFlag)				//如果之前是营业的,关闭窗口营业线程,这就导致程序启动模拟要从营业前的时间段开始
		{
			openFlag = false;
			//关闭窗口线程
			joinAllThreads();		//等待所有的线程关闭
		}
		return;
	}
	else {
		if (!openFlag)				//如果之前是不在营业的,开启窗口营业线程
		{
			openFlag = true;
			//开启窗口线程
			openAllThread();		//开启所有的线程
		}

	}


	Customer customers;

	customers.setEnterTime(sysTime::getInstance().get_HHMM_time());

	//查找排队最短窗口
	CounterWindow* win = getShortWindow(customers.isVIP());

	win->addCustomer(customers);		//添加客户到排队窗口


}

void Bank::joinAllThreads()
{
	//关闭普通窗口
	for (auto it : counterWinVec)
	{
		it.closeConduct();
	}

	//关闭VIP窗口
	for (auto it : counterWinVipVec)
	{
		it.closeConduct();
	}

	for (auto& threadPtr : threads)
	{
		if (threadPtr->joinable())
		{
			threadPtr->join();
		}
	}
}

CounterWindow* Bank::getShortWindow(bool isVIP)
{
	

	CounterWindow* win, * vipWin;		//普通窗口和vip窗口

	if (!isVIP)			//不是VIP分支
	{
		//获取第一个队列的长度,然后以这个长度为基准
		int num = counterWinVec[0].getCustomeQuenNum();
		int counter = 0;


		for (int i = 0; i < counterWinVec.size(); i++)
		{
			int tempNum = counterWinVec[i].getCustomeQuenNum();
			if (tempNum <= num)
			{
				num = tempNum;
				counter = i;
			}
		}
		win = &counterWinVec[counter];

		return win;
	}
	else {				//是VIP分支
		int vipNum = counterWinVipVec[0].getVipCustomQuenNum();
		int vipCount = 0;

		for (int i = 0; i < counterWinVipVec.size(); i++)
		{
			int tempNum = counterWinVipVec[i].getVipCustomQuenNum();
			if (tempNum <= vipNum)
			{
				vipNum = tempNum;
				vipCount = i;
			}
		}
		vipWin = &counterWinVipVec[vipCount];			//获取VIP下标

		//获取第一个队列的长度,然后以这个长度为基准
		int num = counterWinVec[0].getCustomeQuenNum();
		int counter = 0;

		for (int i = 0; i < counterWinVec.size(); i++)
		{
			int tempNum = counterWinVec[i].getVipCustomQuenNum();
			if (tempNum < num)
			{
				num = tempNum;
				counter = i;
			}
		}

		win = &counterWinVec[counter];


		if (win->getVipCustomQuenNum() >= vipWin->getVipCustomQuenNum())
		{
			return vipWin;
		}
		else {
			return win;
		}

	}
}


void Bank::openAllThread()
{
	//启动VIP窗口
	for (int i = 0; i < counterWinVipVec.size(); i++)
	{
		auto threadPtr = std::make_shared<std::thread>([this, i]() {
			counterWinVipVec[i].openConduct();
			counterWinVipVec[i].conductBusiness();		//启动线程开启业务
			});

		threads.push_back(threadPtr);
	}

	//启动窗口
	for (int i = 0; i < counterWinVec.size(); i++)
	{
		auto threadPtr = std::make_shared<std::thread>([this, i]() {
			counterWinVec[i].openConduct();
			counterWinVec[i].conductBusiness();			//启动线程业务
			});
		threads.push_back(threadPtr);
	}
}

        时间系统使用while循环实现的,sysTime类使用了懒汉单例模式,在菜单类调用中初始化。
有一些多线程之类的,智能指针、原子变量、没有上锁,因为没有库里面的读写锁,这里考虑到都是读取时间不会改变时间,就没有加锁。这里通过区间内获取随机数的方式取得一个时间间隔,模模拟顾客进入银行办理业务,Menu类中的start()函数中可以设置客流量的频率。
        练手的代码,有些地方没有处理好,比如程序一开始的时间转换函数,可以使用饿汉单例模式实现,(工具类不会占用很大资源吧)。总之,希望评论区多多指点。感谢(抱拳)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/952209.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新版2024AndroidStudio项目目录结构拆分

如题 下载了最新版的android studio 发现目录结构和以前不一样 自动帮你合并了 如何层层抽丝剥茧呢 按照一下步骤即可解决问题&#xff01;

【Rust自学】11.6. 控制测试运行:并行和串行(连续执行)测试

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 11.6.1. 控制测试的运行方式 cargo test和cargo run一样&#xff0c;cargo test也会编译代码并生成一个二进制文件用于测试&#xff0c;…

linux:文件的创建/删除/复制/移动/查看/查找/权限/类型/压缩/打包

关于文件的关键词 创建 touch 删除 rm 复制 cp 权限 chmod 移动 mv 查看内容 cat(全部); head(前10行); tail(末尾10行); more,less 查找 find 压缩 gzip ; bzip 打包 tar 编辑 sed 创建文件 格式&#xff1a; touch 文件名 删除文件 复制文件 移动文件 查看文…

Docker 基础知识

背景 传统的linux的环境部署 命令多步骤多安装版本多 使用docker的话&#xff0c;一个命令就可以全部搞定 安装linux 之前安装过&#xff0c;所以直接使用的开罩进行复制的如果之前配置过静态地址&#xff0c;需要改成IPV4静态地址访问 安装docker 参考连接&#xff1a;https:/…

Docker 从入门到精通

文章目录 Ubuntu 安装Docker步骤前言1. 进入Docker官网&#xff0c;进入开发者页面2. 选择适合自己的安装方式3. 安装 Docker1.更新系统包&#xff0c;安装插件&#xff0c;创建秘钥及目录2.安装 Docker 软件包3.设置开机启动4.通过运行 hello-world 镜像验证安装是否成功 常见…

概率图模型01

机器学习中&#xff0c;线性回归、树、集成和概率图都属于典型的统计学习方法&#xff0c;概率图模型会更深入地体现出‘统计’两字 概率图模型的常见算法 概率图模型中的图 概率图模型如图主要分为两种&#xff0c;即贝叶斯网络和马尔可夫网络&#xff0c;有向图与无向图&…

Vue Router4

Vue Router 是 Vue.js 官方的路由管理器。Vue Router 基于路由和组件的映射关系&#xff0c;页面路径发生改变&#xff0c;就进行对应的组件切换。 安装&#xff1a; npm install vue-router。 基本 使用&#xff1a; // src/router/index.js import {createRouter, create…

深度学习知识点:LSTM

文章目录 1.应用现状2.发展历史3.基本结构4.LSTM和RNN的差异 1.应用现状 长短期记忆神经网络&#xff08;LSTM&#xff09;是一种特殊的循环神经网络(RNN)。原始的RNN在训练中&#xff0c;随着训练时间的加长以及网络层数的增多&#xff0c;很容易出现梯度爆炸或者梯度消失的问…

通过氧化最小化工艺提高SiC MOSFET迁移率的深入分析

标题 Insight Into Mobility Improvement by the Oxidation-Minimizing Process in SiC MOSFETs&#xff08;TED2024&#xff09; 文章的研究内容 文章的研究内容主要围绕氧化最小化工艺&#xff08;oxidation-minimizing process&#xff09;对碳化硅&#xff08;SiC&…

【Unity小技巧】解决Visual Code中文乱码

在Mac下使用VS Code打开代码时&#xff0c;中文注释显示乱码。 解决方法&#xff1a; VS Code&#xff1a;Setting -> Settings -> 搜索“autoGuessEncoding”&#xff0c;然后勾选上即可。 简体中文的Encoding是GB 2312。

maven 下载依赖 jhash:2.1.2 和对应 jar 包

原文地址 前言 25年新的一年&#xff0c;那就先更新一篇技术文章吧&#xff0c;这个是这几天刚遇到的一个有意思的bug&#xff0c;记录分享一下 原因分析 在使用maven加载一个项目的时&#xff0c;发现maven的依赖一直无法解析&#xff0c;更换阿里云镜像和中央仓库都没办法…

回归预测 | MATLAB基于RF-Adaboost多输入单输出回归预测

回归预测 | MATLAB基于RF-Adaboost多输入单输出回归预测 目录 回归预测 | MATLAB基于RF-Adaboost多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 回归预测 | MATLAB基于RF-Adaboost多输入单输出回归预测。 1.Matlab实现RF-Adaboost随机森林集成学习…

【网络协议】动态路由协议

前言 本文将概述动态路由协议&#xff0c;定义其概念&#xff0c;并了解其与静态路由的区别。同时将讨论动态路由协议相较于静态路由的优势&#xff0c;学习动态路由协议的不同类别以及无类别&#xff08;classless&#xff09;和有类别&#xff08;classful&#xff09;的特性…

基于SSM实现的垃圾分类平台系统功能实现二

一、前言介绍&#xff1a; 1.1 项目摘要 随着城市化进程的加速和居民生活水平的提高&#xff0c;城市生活垃圾的产量急剧增加&#xff0c;给城市环境管理带来了巨大压力。传统的垃圾处理方式&#xff0c;如填埋和焚烧&#xff0c;不仅占用大量土地资源&#xff0c;还可能对环…

如何实现多级缓存?

本文重点说一说在Java应用中&#xff0c;多级缓存如何实现。 多级缓存是比较常见的一种性能优化的手段&#xff0c;一般来说就是本地缓存分布式缓存。 本地缓存一般采用Caffeine和Guava&#xff0c;这两种是性能比较高的本地缓存的框架。他们都提供了缓存的过期、管理等功能。…

美摄科技为企业打造专属PC端视频编辑私有化部署方案

美摄科技&#xff0c;作为视频编辑技术的先行者&#xff0c;凭借其在多媒体处理领域的深厚积累&#xff0c;为企业量身打造了PC端视频编辑私有化部署解决方案&#xff0c;旨在帮助企业构建高效、安全、定制化的视频创作平台&#xff0c;赋能企业内容创新&#xff0c;提升品牌影…

嵌入式C语言:什么是指针?

目录 一、指针的基本概念 1.1. 定义指针 1.2. 赋值给指针 1.3. 解引用指针 1.4. 指针运算 1.5. 空指针 1.6. 函数参数 1.7. 数组和指针 1.8. 示例代码 二、指针在内存中的表示 2.1. 内存地址存储 2.2. 内存模型 2.3. 指针与硬件交互 2.4. 示例代码 三 、指针的重…

计算机网络相关习题整理

第一讲 传输媒介 【知识点回顾】 两种导线可以减小电磁干扰&#xff1a; 双绞线&#xff08;分为非屏蔽双绞线、屏蔽双绞线&#xff09;&#xff08;RJ-45用&#xff09;同轴电缆&#xff08;短距离使用&#xff09;网络通信的基本单位&#xff1a;位&#xff08;bit&#xff…

应急响应之入侵排查(下)

一.进程排查 1.Windows 任务管理器查看 在 Windows 系统中&#xff0c;可通过任务管理器查看进程信息。操作步骤为&#xff1a;在任务管理器界面&#xff0c;于 “查看” 选项中选择 “选择列”&#xff0c;随后添加 “映像路径名称” 和 “命令行”&#xff0c;以此查看更多进…

极狐GitLab 正式发布安全版本17.7.1、17.6.3、17.5.5

本分分享极狐GitLab 补丁版本 17.7.1, 17.6.3, 17.5.5 的详细内容。这几个版本包含重要的缺陷和安全修复代码&#xff0c;我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS&#xff0c;技术团队已经进行了升级&#xff0c;无需用户采取任何…