【服务器编程】多线程安全数据库连接池

30 篇文章 2 订阅
3 篇文章 0 订阅

某风平浪静的晚上,我和多线程搅上劲,如何在多线程环境安全创建连接池、释放、使用,想了很久,写了很久,写出了如下代码,感觉也很多bug吧。

发表此文,谨表示我今晚不知道为什么如此 较真,很想做到完美,可是能力还是不到那个高度,写不出很完美的代码。

如果有想法的同志路过,留点提示给我,谢谢谢谢。

/* 析构不知道怎么在多线程保证安全,程序员责任,我的脑袋要炸了 */
/* 销毁由一个线程完成,而创建和使用是线程安全的连接池 */
class MYSQLConnPool
{
public:
	typedef enum {
		E_MALLOC, 	/* 分配线程池时失败 */
		E_SUM_TO_MAX,	/* 连接池的连接数过大 */
		E_EXIST,		/* 连接池已存在 */
		E_NO_SOURCE,	/* 没有空闲连接 */
		E_INIT_SEM,	/* 初始化信号量错误(也许不该这样) */
		NONERR
	}ERRNO;
public:
	/* Singleton模式*/
	static MYSQLConnPool* CreateConnPool(const char *host, const char *user, const char *psw, const char *db, int conn_num)
	{
		
		pthread_mutex_lock(&m_MutexCreat);

		/* 连接数太大 */
		if (conn_num > MAX_CONN_NUM)
		{
			pthread_mutex_lock(&m_MutexMap);
			m_ErrMap[pthread_self()] = E_SUM_TO_MAX;
			pthread_mutex_unlock(&m_MutexMap);
			pthread_mutex_unlock(&m_MutexCreat);
			return nullptr;
		}
		if (m_ConnPool == nullptr)
		{
			m_ConnPool = new MYSQLConnPool(host, user, psw, db, conn_num);
			if (m_ErrMap[pthread_self()] == E_INIT_SEM)
			{/* 初始化信号量时出错 */
				delete m_ConnPool;
				pthread_mutex_unlock(&m_MutexCreat);
				return nullptr;
			}
			if (m_ConnPool)
			{/* 建立连接池成功 */
				pthread_mutex_unlock(&m_MutexCreat);
				return m_ConnPool;
			}
			/* new失败 */
			pthread_mutex_lock(&m_MutexMap);
			m_ErrMap[pthread_self()] = E_MALLOC;
			pthread_mutex_unlock(&m_MutexMap);
			pthread_mutex_unlock(&m_MutexCreat);
			return nullptr;
		}
		/* 该进程已经生成连接池 */
		pthread_mutex_lock(&m_MutexMap);
		m_ErrMap[pthread_self()] = E_EXIST;
		pthread_mutex_unlock(&m_MutexMap);
		pthread_mutex_unlock(&m_MutexCreat);
		return nullptr;

	}

	/*
	*	试想:一个监听主线程有几个服务线程,而每个服务线程(不同的服务封装了一个类)都想用到这个连接池(一个进程一个连接池),
	*	如果又上层(主线程)传到参数(连接池)到下层(服务线程)方式,那么太破坏服务类的封装了,故定义一个返回池指针的函数。
	*	(针对本开发的项目)
	*/
	static MYSQLConnPool* GetConnPoolPointer()
	{
		return m_ConnPool;
	}

	static ERRNO GetLastError()
	{
		pthread_mutex_lock(&m_MutexMap);
		std::map<pthread_t, ERRNO>::iterator ite;
		ite = map.find(pthread_self());
		if (ite == map.end())
		{
			pthread_mutex_unlock(&m_MutexMap);
			return NONERR;
		}
		ERRNO e = m_ErrMap[pthread_self()];
		pthread_mutex_unlock(&m_MutexMap);
		return e;
	}

	MYSQL *GetConnection()
	{/* 连接池没有空闲连接就阻塞 */	
		sem_wait(&m_ConnNum);
		pthread_mutex_lock(&m_MutexConnVec);
		MYSQL *conn = m_ConnVec.back();
		m_ConnVec.pop_back();
		pthread_mutex_unlock(&m_MutexConnVec);
		return conn;
	}

	void RecoverConnection(MYSQL *conn)
	{
		if (conn)
		{
			pthread_mutex_lock(&m_MutexConnVec);
			m_ConnVec.push_back(conn);
			pthread_mutex_unlock(&m_MutexConnVec);
			sem_post(&m_ConnNum);
		}
	}

	/* 目前没想到如何做到在多线程环境析构而绝对不冲突 */
	/* 虚函数不是原子操作,在多线程环境有可能在析构同时,另外一个线程在使用连接池 */
	~MYSQLConnPool()
	{
		/* 回收所有资源(连接)后,销毁信号量,就不可能再有线程非法操作了 */
		for (int i = 0; i < m_count; ++i)
		{/* 所以,使用了一定要归还啊,不然死的很惨+_+ */
			sem_wait(&m_ConnNum);
		}
		sem_destroy(&m_ConnNum);
		pthread_mutex_destroy(&m_MutexConnVec);
		for (int i = 0; i < m_ConnNum; ++i)
		{
			mysql_close(m_ConnVec[i]);
		}
		m_ConnPool = nullptr;
	}

private:
	MYSQLConnPool(const char *host, const char *user, const char *psw, const char *db, int conn_num);

private:
	/* 根据线程存储了已发生什么错误 */
	pthread_mutex_t m_MutexMap;
	std::map<pthread_t, ERRNO> m_ErrMap;

	/* 连接池最大连接数 */
	static const int MAX_CONN_NUM = 16;

	/* 连接池的连接数 */
	int m_count;
	/* 可用资源的信号量表示 */
	sem_t m_ConnNum;
	pthread_mutex_t m_MutexConnVec;

	/* 连接列表 */
	std::vector<MYSQL*> m_ConnVec;

	/* 多个线程一起创建时,使用互斥防止创建多个 */
	static pthread_mutex_t m_MutexCreat;

	/* 实例指针*/
	static MYSQLConnPool *m_ConnPool;
};

MYSQLConnPool* MYSQLConnPool::m_ConnPool = nullptr;

pthread_mutex_t MYSQLConnPool::m_MutexCreat = PTHREAD_MUTEX_INITIALIZER;


MYSQLConnPool::MYSQLConnPool(const char *host, const char *user, const char *psw, const char *db, int conn_num)
{
	m_count = 0;
	for (int i = 0; i < conn_num; ++i)
	{
		MYSQL *conn = mysql_init(nullptr);
		if (!conn)
			continue;
		conn = mysql_real_connect(conn, host, user, psw, db, 0, nullptr, 0);
		if (!conn)
			continue;
		m_count++;
		m_ConnVec.push_back(conn);
	}
	m_ErrMap[pthread_self()] = NONERR;
	int ret = sem_init(&m_ConnNum, 0, m_ConnVec.size());
	if (ret == -1)
	{
		pthread_mutex_lock(&m_MutexMap);
		m_ErrMap[pthread_self()] = E_INIT_SEM;
		pthread_mutex_unlock(&m_MutexMap);
	}
	m_MutexConnVec = PTHREAD_MUTEX_INITIALIZER;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值