您的位置:首页 > 数据库

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

2016-07-05 23:56 274 查看
某风平浪静的晚上,我和多线程搅上劲,如何在多线程环境安全创建连接池、释放、使用,想了很久,写了很久,写出了如下代码,感觉也很多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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: