大家好,今天书接上回,接着进行简单连接池的实现!https://blog.csdn.net/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~