单例类解决线程池阻塞问题
数据底座节点需要将外部系统的数据同步到本节点,但由于同步的表数据量太大,如果一次性进行多次表数据同步,容易耗完所有的线程。 对此,设置定时任务,每晚12.进行表数据同步,一个表只能由一个线程同步。为此,需实现一个单例的表数据同步管理器。
实现逻辑
通过一个m_TableSync向量记录正在同步的表名,当其他线程需要操作该表时,若通过同步管理器得知其正处于同步中,则放弃本次操作,直到其同步完成。
#ifndef __SYSTABLE_H__
#define __SYSTABLE_H__
#include <vector>
#include <string>
#include <os/thread_mutex.h>
#include <os/thread.h>
#include <Include/esb_message_interface.h>
#include <Include/pack_interface.h>
class CSyncTableManager
{
private:
CSyncTableManager();
virtual ~CSyncTableManager();
public:
static CSyncTableManager* GetInstance();
// 给其他服务提供的接口,查看哪些表处于同步中
std::vector<std::string>& GetAllSyncTable();
// 将一个表设置为同步中
bool setTableSync(std::string tableName);
// 将一个表设置为非同步
bool setTableFinish(const char* tableName);
// 判断一个表是否处于同步中,由于只考虑同步、非同步两种状态,所以返回值用bool不用int
bool IsOneTableSync(const char* tableName);
// 解决锁重入的问题
bool IsOneTableSyncNoLock(const char* tableName);
// 判断要同步的表是否全部在同步状态,有一个在同步状态,就返回false
bool IsAllTableNoSync(IF2UnPacker * lpInUnPacker, IF2Packer * lpOutPacker);
private:
static CSyncTableManager* m_CSyncTableManager;
std::vector<std::string> m_TableSync;
FBASE2::CThreadMutex m_SyncTableMutex;
};
#endif
重入锁问题解决
在IsAllTableNoSync函数中,加了一把自动释放的锁m_SyncTableMutex,在判断当前表v_cur_table_name状态时,由于原本调用的是IsOneTableSync函数,也加锁了, 这样导致v_cur_table_name被两把锁锁了,出现报错。因此,引入无锁函数IsOneTableSyncNoLock解决重入锁问题。
bool CSyncTableManager::IsOneTableSync(const char* sTableName){
FBASE2::CAutoMutex lock(&m_SyncTableMutex);
return IsOneTableSyncNoLock(sTableName);
}
bool CSyncTableManager::IsOneTableSyncNoLock(const char* sTableName){
return find(m_TableSync.begin(), m_TableSync.end(), sTableName)!=m_TableSync.end();
}
bool CSyncTableManager::IsAllTableNoSync(IF2UnPacker * lpInUnPacker, IF2Packer * lpOutPacker){
FBASE2::CAutoMutex lock(&m_SyncTableMutex);
bool bRes=true;
char v_cur_table_name[1024]={0};
lpOutPacker->AddField("table_name",'S',1024);
while(!lpInUnPacker->IsEOF()){
strcpy(v_cur_table_name, lpInUnPacker->GetStr("table_name"));
// 记录下同步中的表,返回给客户端
if(IsOneTableSyncNoLock(v_cur_table_name)){ // 解决重入锁问题
lpOutPacker->AddStr(v_cur_table_name);
bRes = false;
}
lpInUnPacker->Next();
}
return bRes;
}
tip:
- 能在
GetInstance中直接使用锁吗?
- 不能,因为m_SyncTableMutex同步锁是在对象中分配的,而GetInstance并没有对象的this指针,根本就找不到同步锁的地址。
- 为什么
GetInstance中不需要加锁?
m_CSyncTableManager是static变量,全局唯一
// 在主线程中初始化一个数据表同步管理器
CSyncTableManager *CSyncTableManager::m_CSyncTableManager = new CSyncTableManager();
// 子线程 获取数据表同步管理器
// 如果多个子线程都获取到了这个管理器,也没关系,会判断当前子线程同步的表如果在同步中,就直接退出了
CSyncTableManager *CSyncTableManager::GetInstance()
{
return m_CSyncTableManager;
}