当前位置: 首页 > article >正文

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()函数中可以设置客流量的频率。
        练手的代码,有些地方没有处理好,比如程序一开始的时间转换函数,可以使用饿汉单例模式实现,(工具类不会占用很大资源吧)。总之,希望评论区多多指点。感谢(抱拳)。


http://www.kler.cn/a/472575.html

相关文章:

  • 1.2.1-2部分数据结构的说明02_链表
  • 案例解读 | 香港某多元化综合金融企业基础监控+网管平台建设实践
  • mysql -> 达梦数据迁移(mbp大小写问题兼容)
  • C++ 入门第23天:Lambda 表达式与标准库算法入门
  • 使用免费内网穿透(p2p)网络环境搭建小型文件管理服务器(简单操作)
  • 【VUE+ElementUI】通过接口下载blob流文件设置全局Loading加载进度
  • 单片机-定时器中断
  • C++ 复习总结记录五
  • (k8s)kubectl不断重启问题解决!
  • 代码随想录算法训练营第二十七天-贪心算法-455. 分发饼干
  • 技术速递|通过 .NET Aspire 使用本地 AI 模型
  • 【大模型+本地自建知识图谱/GraphRAG/neo4j/ollama+Qwen千问(或llama3)】 python实战(中)
  • 支持各大平台账单处理,支持复杂业财数据的精细化对账|商派OMS
  • 将java前后端项目和使用了conda虚拟环境的python项目添加到ubuntu服务
  • python中的列表推导式详解
  • 华灯已上:夜色跌宕绘情谱
  • 【AI日记】25.01.08
  • PLC实现HTTP协议JSON格式数据上报对接的参数配置说明
  • OBS Zoom to Mouse 脚本安装与使用指南
  • MySQL UDF提权
  • 1-【选修】逻辑回归
  • 2025新春烟花代码(二)HTML实现孔明灯和烟花效果
  • SpringBoot 使用 Cache 集成 Redis做缓存保姆教程
  • 能不能在家部署一个硬件实现远程唤醒局域网内所有电脑?
  • 从零手写实现redis(三)内存数据如何重启不丢失?
  • Spring Boot 项目自定义加解密实现配置文件的加密