C++实现简易连接池(详细版)下

大家好,今天书接上回,接着进行简单连接池的实现!https://blog.youkuaiyun.com/csdddd55/article/details/148928680?spm=1001.2014.3001.5501

连接池

生产连接的线程(生产者线程)

//线程生产函数
void ConnectionPool::produceconnections() {
	while (true) {
		unique_lock<mutex> lock(_queueMutex);//进来便加锁(这是互斥访问)
		//不为空就不生产,等待
		while (!_pool.empty()) {
			_Empty.wait(lock);

		}
		//未达最大连接数,生产连接
		if (_currentSize < _maxSize) {
			//生产连接,放入连接池
			Connection* conn=new Connection();
			conn->connect(_ip, _port, _username, _password, _dbname);
			//只要连接进入连接池就要重置空闲时间
			conn->resetFreeTime();
			_pool.push(conn);
			_currentSize++;
		}
		//通知消费线程
		//同步作用
		_Empty.notify_all();
	}
	
}

消费连接的线程(消费者线程)

//获得线程的接口实现
shared_ptr<Connection> ConnectionPool::getConnection() {
	unique_lock<mutex> lock(_queueMutex);
	while (_pool.empty()) {//队列为空,没有可用连接,当然就是再wait
		//队列没有可用conn那就等待
		if (cv_status::timeout == _Empty.wait_for(lock, chrono::milliseconds(_ConnectionTimeOut))) {
			//这里将timeout和wait_for返回值进行比对是个细节,因为只有时间真得超时了才来看看是不是
			//队列空了,进而判断是否报错
			//如果是中间未超时,醒来,但是被其他线程抢走了资源,则不算超时报错,这个if就是防止
			//这个情况发生
			if (_pool.empty()) {
				LOG("wait for connection timeout");
				return nullptr;
			}
		}
	}
	//智能指针出了作用域就会释放,但是是直接delete
	//我们希望它被delete吗?当然不啊,因为我们还要把它放回连接池呢
	//所有需要自定义deleter,那就是用lamada函数进行
	//不了解lamada就去学习一下,这里不展开叙述,不是很难
	shared_ptr<Connection> conn(_pool.front(), [&](Connection* p) {
		//访问队列就要互斥访问
		unique_lock<mutex> lck(_queueMutex);

		_pool.push(p);
		p->resetFreeTime();
		
		});
	_pool.pop();
	//消费了就唤醒生产者线程,反正它执不执行也会判断
	_Empty.notify_all();
	return conn;


}

注意看代码里的注意奥,里面有详细解释,我在外面就不过多赘述了。感谢理解

清理线程

//清理线程
void ConnectionPool::cleanconnections() {
	while (true) {
		//当然我们没必要每时每刻都在扫描,每过最大空闲时间扫描一次即可
		this_thread::sleep_for(chrono::seconds(_maxIdleTime));
		//老话畅谈:共享资源访问互斥访问
		unique_lock<mutex> lock(_queueMutex);
		//只有当前连接数量多于初始连接量才会出发这个被动(清理)
		while (_currentSize > _initSize) {
			//首先便利连接队列,超时的就pop就完了
			Connection* conn = _pool.front();
			if (conn->getfreeTime() >= _maxIdleTime * 1000) {
				_pool.pop();
				delete conn;
				_currentSize--;
			}
			else {
				break;
			}
		}
	}
}

oh!差点忘了,我这把连接类也说一下,光顾着说连接池类了,谁让连接池是主角呢。

connection

class Connection {
public:
    Connection();
    ~Connection();
    //连接数据库
    bool connect(string ip, unsigned short port, string username, string password, string dbname);
    //更新数据库
    bool update(string sql);
    //查询数据库
    MYSQL_RES* query(string sql);
    //该函数负责获取空闲时间
    clock_t getfreeTime() {
        return clock() - freeTime;
    }
    //重置空闲时间为当前clock
    void resetFreeTime() {
        freeTime = clock();
    }
private:
    MYSQL* _conn;
    clock_t freeTime;//空闲时间
    bool newbuilded=false;
};

这里说明一下,连接类主要设计的是MySQL编程相关的东西,如果你觉得看不懂,没关系,本项目只是涉及一部分,不难理解,就趁这个机会进行学习MySQL吧。

构造与析构函数

Connection::Connection() {
	//分配指针
	_conn=mysql_init(NULL);
}
Connection::~Connection() {
	//释放指针
	//关闭连接
	if (_conn != NULL) {
		mysql_close(_conn);
	}
}

其他成员函数

bool Connection::connect(string ip, unsigned short port, string username, string password, string dbname) {
	//连接数据库
	MYSQL *conn = mysql_real_connect(_conn, ip.c_str(), username.c_str(), password.c_str(), dbname.c_str(), port, NULL, 0);
	return conn!= NULL;
}

bool Connection::update(string sql) {
	if (mysql_query(_conn, sql.c_str())) {
		LOG("更新失败:" + sql);
		return false;
	}
	return true;
}

MYSQL_RES* Connection::query(string sql) {
	if (mysql_query(_conn, sql.c_str())) {
		LOG("查询失败:" + sql);
		return NULL;
	}
	return mysql_use_result(_conn);
}

至此基本大功告成!你就基本完成了本项目啦~

完整代码

一定有些小伙伴出于某种原因不能跑通这个项目,当然也有一些朋友可能有些着急,那么完整代码附上,请签收!

我将按照我在上篇帖子中的代码结构进行给出代码,大家只需要创建好相应文件即可,然后点击运行!(本人是vs2022平台)

ConnectionPool.h

#pragma once
#include <string>
#include <queue>
#include "Connection.h"
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <memory>
using namespace std;
/*---------------------------------------*/
/*----------实现连接池功能---------------*/
/*---------------------------------------*/


//单例模式实现
class ConnectionPool {
public:
	static ConnectionPool* getConnectionPool();
	//用户接口,获得连接来与数据库进行交互
	shared_ptr<Connection> getConnection();
private:
	bool loadConfig();
	void produceconnections();//产生连接
	void cleanconnections();//清理空闲连接
	ConnectionPool();
	
	
	//内部属性
	string _ip;
	unsigned int _port;
	string _username;
	string _password;
	string _dbname;
	int _initSize;
	int _maxSize;
	int _maxIdleTime;
	int _ConnectionTimeOut;

	queue<Connection*> _pool;
	mutex _queueMutex;
	atomic_int _currentSize;
	condition_variable _Empty;

};

ConnectionPool.cpp


#include "ConnectionPool.h"
#include "public.h"
#include <functional>
#include <thread>
#include <memory>
#include <condition_variable>
ConnectionPool::ConnectionPool() {
	if (!loadConfig()) {
		LOG("load mysqlConfg.ini failed");
		return;
	}
	//创建初始连接
	for (int i = 0; i < _initSize; i++) {
		Connection* conn = new Connection();
		conn->connect(_ip, _port, _username, _password, _dbname);
		_pool.push(conn);
		conn->resetFreeTime();
		_currentSize++;
	}
	//启动生产CONN线程
	thread producer(bind(&ConnectionPool::produceconnections, this));
	producer.detach();
	//启动清理线程,将超过最大空闲时间的连接清理掉
	thread cleaner(bind(&ConnectionPool::cleanconnections, this));
	cleaner.detach();


}

//清理线程
void ConnectionPool::cleanconnections() {
	while (true) {
		//当然我们没必要每时每刻都在扫描,每过最大空闲时间扫描一次即可
		this_thread::sleep_for(chrono::seconds(_maxIdleTime));
		//老话畅谈:共享资源访问互斥访问
		unique_lock<mutex> lock(_queueMutex);
		//只有当前连接数量多于初始连接量才会出发这个被动(清理)
		while (_currentSize > _initSize) {
			//首先便利连接队列,超时的就pop就完了
			Connection* conn = _pool.front();
			if (conn->getfreeTime() >= _maxIdleTime * 1000) {
				_pool.pop();
				delete conn;
				_currentSize--;
			}
			else {
				break;
			}
		}
	}
}
//线程生产函数
void ConnectionPool::produceconnections() {
	while (true) {
		unique_lock<mutex> lock(_queueMutex);//进来便加锁(这是互斥访问)
		//不为空就不生产,等待
		while (!_pool.empty()) {
			_Empty.wait(lock);

		}
		//未达最大连接数,生产连接
		if (_currentSize < _maxSize) {
			//生产连接,放入连接池
			Connection* conn=new Connection();
			conn->connect(_ip, _port, _username, _password, _dbname);
			//只要连接进入连接池就要重置空闲时间
			conn->resetFreeTime();
			_pool.push(conn);
			_currentSize++;
		}
		//通知消费线程
		//同步作用
		_Empty.notify_all();
	}
	
}


//获得线程的接口实现
shared_ptr<Connection> ConnectionPool::getConnection() {
	unique_lock<mutex> lock(_queueMutex);
	while (_pool.empty()) {//队列为空,没有可用连接,当然就是再wait
		//队列没有可用conn那就等待
		if (cv_status::timeout == _Empty.wait_for(lock, chrono::milliseconds(_ConnectionTimeOut))) {
			//这里将timeout和wait_for返回值进行比对是个细节,因为只有时间真得超时了才来看看是不是
			//队列空了,进而判断是否报错
			//如果是中间未超时,醒来,但是被其他线程抢走了资源,则不算超时报错,这个if就是防止
			//这个情况发生
			if (_pool.empty()) {
				LOG("wait for connection timeout");
				return nullptr;
			}
		}
	}
	//智能指针出了作用域就会释放,但是是直接delete
	//我们希望它被delete吗?当然不啊,因为我们还要把它放回连接池呢
	//所有需要自定义deleter,那就是用lamada函数进行
	//不了解lamada就去学习一下,这里不展开叙述,不是很难
	shared_ptr<Connection> conn(_pool.front(), [&](Connection* p) {
		//访问队列就要互斥访问
		unique_lock<mutex> lck(_queueMutex);

		_pool.push(p);
		p->resetFreeTime();
		
		});
	_pool.pop();
	//消费了就唤醒生产者线程,反正它执不执行也会判断
	_Empty.notify_all();
	return conn;


}
//线程安全

ConnectionPool* ConnectionPool::getConnectionPool() {
	static ConnectionPool pool;
	return &pool;
}

bool ConnectionPool::loadConfig() {
	FILE* fp = fopen("mysqlConfg.ini", "r");
	if (fp == NULL) {
		LOG("mysqlConfg.ini not found");
		return false;
	}
	while (!feof(fp)) {
		char line[1024] = { 0 };
		fgets(line, 1024, fp);
		string str = line;
		int idx = str.find("=",0);
		if (idx == -1) {
			continue;
		}
		int enidx = str.find("\n", idx);
		string key = str.substr(0, idx);
		string value = str.substr(idx + 1, enidx - idx - 1);
		if (key == "ip") {
			_ip = value;
		}
		else if (key == "port") {
			_port = atoi(value.c_str());
		}
		else if (key == "username") {
			_username = value;
		}
		else if (key == "password") {
			_password = value;
		}
		else if (key == "dbname") {
			_dbname = value;
		}
		else if (key == "initSize") {
			_initSize = atoi(value.c_str());
		}
		else if (key == "maxSize") {
			_maxSize = atoi(value.c_str());
		}
		else if (key == "maxIdleTime") {
			_maxIdleTime = atoi(value.c_str());
		}
		else if (key == "connectionTimeout") {
			_ConnectionTimeOut= atoi(value.c_str());
		}
		return true;
	}

}

Connection.h

#pragma once
#include <string>
#include <mysql.h>
#include <ctime>
using namespace std;
/*---------------------------------------*/
/*----------实现MYSQL操作----------------*/
/*---------------------------------------*/

class Connection {
public:
    Connection();
    ~Connection();
    //连接数据库
    bool connect(string ip, unsigned short port, string username, string password, string dbname);
    //更新数据库
    bool update(string sql);
    //查询数据库
    MYSQL_RES* query(string sql);
    clock_t getfreeTime() {
        return clock() - freeTime;
    }
    void resetFreeTime() {
        freeTime = clock();
    }
private:
    MYSQL* _conn;
    clock_t freeTime;
    bool newbuilded=false;
};

Connection.cpp


#include "Connection.h"
#include "public.h"

Connection::Connection() {
	//分配指针
	_conn=mysql_init(NULL);
}
Connection::~Connection() {
	//释放指针
	//关闭连接
	if (_conn != NULL) {
		mysql_close(_conn);
	}
}
bool Connection::connect(string ip, unsigned short port, string username, string password, string dbname) {
	//连接数据库
	MYSQL *conn = mysql_real_connect(_conn, ip.c_str(), username.c_str(), password.c_str(), dbname.c_str(), port, NULL, 0);
	return conn!= NULL;
}

bool Connection::update(string sql) {
	if (mysql_query(_conn, sql.c_str())) {
		LOG("更新失败:" + sql);
		return false;
	}
	return true;
}

MYSQL_RES* Connection::query(string sql) {
	if (mysql_query(_conn, sql.c_str())) {
		LOG("查询失败:" + sql);
		return NULL;
	}
	return mysql_use_result(_conn);
}

mysqlConfg.ini

#配置文件
ip=127.0.0.1
port=3306
#这里修改为你的用户名
username=root
#这里注意修改为你的密码
password=200319
#这里修改为你的数据库名
dbname=chat

initSize=10
maxSize=1024
#单位秒
maxIdleTime=60
#单位毫秒
connectionTimeout=100

public.h

#pragma once
#include <iostream>
#define LOG(str)\
    cout<<__FILE__<<" : "<<__LINE__<<" : "<<\
    __TIMESTAMP__<<" : "<<str<<endl;

main.cpp

#include <iostream>
#include "Connection.h"
#include "ConnectionPool.h"
using namespace std;


int main() {
	ConnectionPool pool(); // create a connection pool with 5 connections
	return 0;
}

这里楼主偷了一个懒在main函数,你可用自己定的测试代码!

本次结束了,再见!如果对你有个帮助,请留下你的点赞吧,有问题可以评论区留言~

bye~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值