【数据库连接池】02:连接池生产者操作

连接池生产者操作


OVERVIEW

  • 连接池生产者操作
      • 1.需求分析
      • 2.CommonConnectionPool.h
      • 3.CommonConnectionPool.cpp

1.需求分析

数据库连接池还需要向外提供以下接口:

  • 创建一个生产者线程,作为数据库连接的生产者,

    在创建生产者线程connProducer时,需要传入一个线程函数produceConnTask,

    thread connProducer(std::bind(&ConnectionPool::produceConnTask, this));
    

    对于普通成员方法的调用依赖于对象,如果要将其设计成为一个线程函数,就必须将this指针所需要的对象绑定到该方法上(线程函数都是C接口,不支持面向对象),

    使用绑定器为当前对象的成员方法produceConnTask,绑定一个当前对象,

    std::bind(&ConnectionPool::produceConnTask, this);
    
  • produceConnTask具体的执行逻辑,

    将线程函数写成类的成员函数,最大的好处是可以非常方便的访问当前对象的成员变量,

    • 当队列为空时,消费者线程通知 notify 生产者开始生产连接,这涉及线程间的通信操作,
    • 如果队列中的连接数量已经大于连接池最大连接数量时,需要停止生产,
    // 生产者线程的线程函数
    void ConnectionPool::produceConnTask()
    {
    	for (;;) {
    		// 队列上锁(自动解锁)
    		unique_lock<mutex> lock(_queueMutex);
    		// 若队列不空 生产者线程进入等待状态 并且将锁释放 等待为空
    		while (!_connectionq.empty()) {
    			condv.wait(lock);
    		}
    		// 连接数量没有到达上限 则继续创建新的连接
    		if (_connectionCnt < _maxSize) {
    			Connection *p = new Connection();
    			p->connect(_ip, _port, _username, _password, _dbname);
    			_connectionq.push(p);
    			_connectionCnt++;
    		}
    		// 通知消费者线程可以消费连接了
    		condv.notify_all();
    	}
    }
    

2.CommonConnectionPool.h

#ifndef _COMMONCONNECTIONPOOL_H
#define _COMMONCONNECTIONPOOL_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "Connection.h"
using namespace std;

// 实现连接池功能模块
class ConnectionPool {
public:
	// 获取连接池对象实例
	static ConnectionPool* getConnectionPool();
	// 生产新连接任务函数
	void produceConnTask();
	// 获取数据库连接
	shared_ptr<Connection> getConnection();
private:
	ConnectionPool();
	bool loadConfigFile();
	// 连接池参数
	int _initSize;				// 初始连接量
	int _maxSize;				// 最大连接量
	int _maxIdleTime;			// 最大空闲时间
	int _connectionTimeout;		// 连接超时时间
	// 数据库信息
	string _ip;
	unsigned short _port;
	string _username;
	string _password;
	string _dbname;
	// 数据库连接存储队列
	queue<Connection*> _connectionq;		// 存储mysql连接的队列
	mutex _queueMutex;						// 维护连接队列的线程安全的互斥锁
	atomic_int _connectionCnt;				// 创建连接的数量 ++操作是线程安全的
	condition_variable condv;					// 条件变量 用于连接生产线程和连接消费线程间的通信
};

#endif

3.CommonConnectionPool.cpp

#include "public.h"
#include "CommonConnectionPool.h"

// 懒汉单例模式
// 对于静态局部变量的初始化 由编译器自动进行 lock与unlock
ConnectionPool* ConnectionPool::getConnectionPool()
{
	static ConnectionPool pool;
    return &pool;
}

// 生产者线程的线程函数
void ConnectionPool::produceConnTask()
{
	for (;;) {
		// 队列上锁(自动解锁)
		unique_lock<mutex> lock(_queueMutex);
		// 若队列不空 生产者线程进入等待状态 并且将锁释放 等待为空
		while (!_connectionq.empty()) {
			condv.wait(lock);
		}
		// 连接数量没有到达上限 则继续创建新的连接
		if (_connectionCnt < _maxSize) {
			Connection *p = new Connection();
			p->connect(_ip, _port, _username, _password, _dbname);
			_connectionq.push(p);
			_connectionCnt++;
		}
		// 通知消费者线程可以消费连接了
		condv.notify_all();
	}
}

// 消费者线程的线程函数
shared_ptr<Connection> ConnectionPool::getConnection()
{
    return shared_ptr<Connection>();
}

// 初始化连接池
ConnectionPool::ConnectionPool() {
	// 1.加载配置项
	if (!loadConfigFile()) return;
	// 2.创建初始数量的连接
	for (int i = 0; i < _initSize; ++i) {
		Connection *p = new Connection();
		p->connect(_ip, _port, _username, _password, _dbname);
		_connectionq.push(p);
		_connectionCnt++;
	}
	// 3.启动一个新的线程作为连接的生产者
	thread connProducer(std::bind(&ConnectionPool::produceConnTask, this));
}

// 读取配置文件
bool ConnectionPool::loadConfigFile()
{
	FILE *fp = fopen("config.conf", "r");
	if (fp == nullptr) {
		LOG("Config file error, file not exists!");
		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 edx = str.find('\n', 0);
		string key = str.substr(0, idx);
		string value = str.substr(idx + 1, edx - 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;
}

你可能感兴趣的:(teamtalk源码解读,数据库)