单例类解决线程池阻塞问题

65 阅读2分钟

单例类解决线程池阻塞问题

数据底座节点需要将外部系统的数据同步到本节点,但由于同步的表数据量太大,如果一次性进行多次表数据同步,容易耗完所有的线程。 对此,设置定时任务,每晚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:

  1. 能在GetInstance中直接使用锁吗?
  • 不能,因为m_SyncTableMutex同步锁是在对象中分配的,而GetInstance并没有对象的this指针,根本就找不到同步锁的地址。
  1. 为什么GetInstance中不需要加锁?

m_CSyncTableManager是static变量,全局唯一

// 在主线程中初始化一个数据表同步管理器
CSyncTableManager *CSyncTableManager::m_CSyncTableManager = new CSyncTableManager();

// 子线程 获取数据表同步管理器
// 如果多个子线程都获取到了这个管理器,也没关系,会判断当前子线程同步的表如果在同步中,就直接退出了
CSyncTableManager *CSyncTableManager::GetInstance()
{
    return m_CSyncTableManager;
}