AceDB 7.8.5版本中数据库多线程和单线程访问的方法
#define ACEDB_ENABLE_CONCURRENCY
#endif
#ifdef ACEDB_ENABLE_CONCURRENCY
#ifndef ACEDB_ENABLE_MULTIHANDLE
#define ACEDB_ENABLE_MULTIHANDLE
1.数据库初始化
Mocha平台在系统bootloader时会首先初始化AceDB数据库, 数据库分为卷一和卷二,卷一为主卷. 首先先打开一次数据库,即打开卷一,如果成功,就继续连接卷二;如果不成功就新建数据库(新建卷一和卷二).
DmMgr.C 代码片段:
------------------------------------------------
rc = ace_OpenDB(DB2_DEFAULT_DB2_PATH, szDBName, NULL, &gDb2Handle[nConnection].pDB);
SysDebug((MID_EXCEPTION, "DbConnect: ace_OpenDB result(%d) %x\n",rc, gDb2Handle[nConnection].pDB));
if (rc != ACE_E_SUCCESS)
{
rc = ace_CreateDB(DB2_DEFAULT_DB2_PATH, szDBName, NULL, &gDb2Handle[nConnection].pDB);
if (rc != ACE_E_SUCCESS)
{
SysDebug((MID_EXCEPTION, "DbConnect: ace_CreateDB rc= %d pDB = %x\n",rc, gDb2Handle[nConnection].pDB));
return FALSE;
}
else
{
AcSprintf(szQuery,"create volume '/DB' 'unlimitdb' as vol2");
SysDebug((ALL_MIDS, "query : %s", szQuery));
rc = ace_ExecSQL(gDb2Handle[nConnection].pDB, szQuery);
SysDebug((MID_EXCEPTION, "DbConnect: ace_ExecSQL create result: %d!",rc));
SysAssertf( rc ==ACE_E_SUCCESS, ("DbConnect : Create Volume2 Failed (%d)\n", rc));
}
}
else
{
AcSprintf(szQuery,"attach volume '/DB' 'unlimitdb' as vol2");
SysDebug((ALL_MIDS, "query : %s", szQuery));
rc = ace_ExecSQL(gDb2Handle[nConnection].pDB, szQuery);
SysDebug((MID_EXCEPTION, "DbConnect: ace_ExecSQL attach result: %d!",rc));
SysAssertf( rc == ACE_E_SUCCESS, ("DbConnect : Attach Volume2 Failed (%d)\n", rc));
}
SysDebug((MID_EXCEPTION, "DbConnect: ace_OpenDB result(%d) %x\n",rc, gDb2Handle[nConnection].pDB));
if (rc != ACE_E_SUCCESS)
{
rc = ace_CreateDB(DB2_DEFAULT_DB2_PATH, szDBName, NULL, &gDb2Handle[nConnection].pDB);
if (rc != ACE_E_SUCCESS)
{
SysDebug((MID_EXCEPTION, "DbConnect: ace_CreateDB rc= %d pDB = %x\n",rc, gDb2Handle[nConnection].pDB));
return FALSE;
}
else
{
AcSprintf(szQuery,"create volume '/DB' 'unlimitdb' as vol2");
SysDebug((ALL_MIDS, "query : %s", szQuery));
rc = ace_ExecSQL(gDb2Handle[nConnection].pDB, szQuery);
SysDebug((MID_EXCEPTION, "DbConnect: ace_ExecSQL create result: %d!",rc));
SysAssertf( rc ==ACE_E_SUCCESS, ("DbConnect : Create Volume2 Failed (%d)\n", rc));
}
}
else
{
AcSprintf(szQuery,"attach volume '/DB' 'unlimitdb' as vol2");
SysDebug((ALL_MIDS, "query : %s", szQuery));
rc = ace_ExecSQL(gDb2Handle[nConnection].pDB, szQuery);
SysDebug((MID_EXCEPTION, "DbConnect: ace_ExecSQL attach result: %d!",rc));
SysAssertf( rc == ACE_E_SUCCESS, ("DbConnect : Attach Volume2 Failed (%d)\n", rc));
}
------------------------------------------------
2. 数据库的访问
Mocha平台是一个多线程平台,上层的APP代码都是按照多线程机制编写的,因此默认情况下要求数据库支持多线程访问.Samsung AceDB
数据库是支持多线程访问的,在头文件中有相应的宏开关可以切换多线程和单线程
AceDB_Common.H 代码片段:
------------------------------------------------
#ifndef ACEDB_ENABLE_CONCURRENCY#define ACEDB_ENABLE_CONCURRENCY
#endif
#ifdef ACEDB_ENABLE_CONCURRENCY
#ifndef ACEDB_ENABLE_MULTIHANDLE
#define ACEDB_ENABLE_MULTIHANDLE
#endif
------------------------------------------------
(1) 多线程访问数据库
在多线程情况下,数据库提供了gDb2Handle结构体,来保存不同线程所对应的数据库操作句柄.应用程序在访问数据库时要先获得当前任务的线程号即Task Number,通过线程号去获得gDb2Handle结构体中与该线程相对应的数据库操作句柄,这个句柄初始为空,只有当打开过数据库后才会被赋值。
在Mocha平台获得线程号,可以通过系统级函数OsGetCurrentTask()来直接获取.因此AceDB在获得线程号的子函数当中,添加了数据库多线程操作的初始化.当获得完线程号后,判断该线程对应的数据库句柄是否被赋值,也就是判断数据库在改线程上是否已经打开了,如果当前线程已经打开了数据库,就直接返回线程号;如果没有就进行数据库打开操作.
DbMgr.C 代码片段:
------------------------------------------------
.....
OsAcquireMutex(hDb2Mutex);
nConnection= __Db2GetCurrentTask();
if(gDb2Handle[nConnection].pDB!= NULL)
{
OsReleaseMutex(hDb2Mutex);
return nConnection;
}
nConnection= __Db2GetCurrentTask();
if(gDb2Handle[nConnection].pDB!= NULL)
{
OsReleaseMutex(hDb2Mutex);
return nConnection;
}
OsReleaseMutex(hDb2Mutex);
.....
rc = ace_OpenDB(DB2_DEFAULT_DB2_PATH, DB2_DEFAULT_DB2_NAME, NULL, &(gDb2Handle[nConnection].pDB));
if (rc != ACE_E_SUCCESS)
{
while(rc != ACE_E_SUCCESS)
{
rc = ace_OpenDB(DB2_DEFAULT_DB2_PATH, DB2_DEFAULT_DB2_NAME, NULL, &(gDb2Handle[nConnection].pDB));
OsSleep(50);
}
}
if (rc != ACE_E_SUCCESS)
{
while(rc != ACE_E_SUCCESS)
{
rc = ace_OpenDB(DB2_DEFAULT_DB2_PATH, DB2_DEFAULT_DB2_NAME, NULL, &(gDb2Handle[nConnection].pDB));
OsSleep(50);
}
}
return nConnection;
------------------------------------------------
AceDB多线程访问数据库还提供了gdb2info以及 gdb2stmt两个结构体来保存对应线程的数据库操作资源,如保存SQL语句的Query变量,保存动态结果集的bindres等等.
总结:在数据库移植过程中对比AceDB和DB2发现,DB2访问数据库的方法和AceDB基本相同.因此可以得出,在这两种数据中进行多线程访问和操作主要分为以下几个步骤:
<1> 程序开始获得线程号.
<2> 判断数据库是否已经在该线程上打开,如果没有打开就进行打开操作.
<3> 根据线程号使用对应的数据库结构体访问和操作数据库.
(1) 单线程访问数据库
对于一个多线程环境的开发平台,要在上层应用是多线程开发的情况下,使APP单线程访问数据库,就需要使用系统的消息队列机制.
对于Mocha平台,Mocha作为主线程,我们希望所有的数据库操作都要使用Mocha这个线程来进行.在AceDB中实现单线程的作法如下:
<1> 初始化数据库,在Mocha线程上产生唯一的一个数据库访问句柄.
<2> 应用程序开始获得线程号
<3> 判断该线程是否为Mocha线程,如果是Mocha线程,就直接使用Mocha线程的数据库操作句柄对数据库进行访问和操作;如果不是Mocha线程,就将当前将要进行的数据库操作的相关信息(如线程号、数据库操作类型、结果集地址、其他需要传入或者传出的参数等)以消息机制发送到Mocha线程的消息队列里,等待Mocha线程去执行.
<4> 等待执行完成后,返回执行的结果.
DbMgrDynamic.C 代码片段:
------------------------------------------------
.....
if( nConnection != OsGetTaskByName("Mocha") )
{
Db2Count Result;
DBQueryInfo *pQueryInfo = MemAlloc( sizeof( DBQueryInfo ) );
pQueryInfo->Connection = nConnection;
pQueryInfo->DBQueryType = DYNAMIC_EXEC;
pQueryInfo->hStmt = hStmt;
pQueryInfo->pResult = &Result;
OsAcquireMutex(hAceDBSingleMutex);
_Db2MochaTaskSendRequest( pQueryInfo );
OsReleaseMutex(hAceDBSingleMutex);
MemFree( pQueryInfo );
return Result;
}
{
Db2Count Result;
DBQueryInfo *pQueryInfo = MemAlloc( sizeof( DBQueryInfo ) );
pQueryInfo->Connection = nConnection;
pQueryInfo->DBQueryType = DYNAMIC_EXEC;
pQueryInfo->hStmt = hStmt;
pQueryInfo->pResult = &Result;
OsAcquireMutex(hAceDBSingleMutex);
_Db2MochaTaskSendRequest( pQueryInfo );
OsReleaseMutex(hAceDBSingleMutex);
MemFree( pQueryInfo );
return Result;
}
.....
------------------------------------------------
DbMgr.C 代码片段:
------------------------------------------------
.....void * _Db2MochaTaskSendRequest( DBQueryInfo *pDBInfo ){void *pResult = NULL;WmDBEvent *DBEvent = (WmDBEvent *)MemAlloc(sizeof(WmDBEvent));
DBEvent->type = EV_DB_REQUEST;DBEvent->pDBInfo = pDBInfo;DBEvent->hDestEventHandler = ALL_MAIN_EVENT_HANDLERS;OsSendMessage(OsGetTaskByName("Mocha"),(OsMessage*)DBEvent);OsAcquireSemaphore( gDb2Sem, INFINITE );SysDebug((MID_EXCEPTION,"OsAcquireSemaphore Connection=%d",pDBInfo->Connection));pResult = pDBInfo->pResult;MemFree( DBEvent );
return pResult;
BOOL _Db2MochaTaskDBEventhandler(WmDBEvent *pDBEvent){DBQueryInfo *pDBInfo;pDBInfo =(DBQueryInfo* )pDBEvent->pDBInfo;switch( pDBInfo->DBQueryType ){.....case DYNAMIC_EXEC :{*((Db2Count *)pDBInfo->pResult) = Db2StmtExecuteExec( pDBInfo->Connection, pDBInfo->hStmt );break;}.....}OsReleaseSemaphore( gDb2Sem );return TRUE;}.....
------------------------------------------------
EmEventQueue.C 代码片段:
------------------------------------------------
BOOL__WmProcessEventInternal(WmEvent* pEvent){.....#ifdef ACEDB_SINGLE_THREAD_VERSIONif( pEvent->any.type == EV_DB_REQUEST ){extern BOOL _Db2MochaTaskDBEventhandler(WmDBEvent *pDBEvent);OsAcquireMutex(pSelf->hEmMutex);_Db2MochaTaskDBEventhandler((WmDBEvent *)pEvent );OsReleaseMutex(pSelf->hEmMutex);return TRUE;}#endif....}
------------------------------------------------
AceDB在多线程平台中单线程访问,特别需要注意两个地方:信号量和互斥锁.
<1> Semaphore
信号量的使用是对系统Mocha线程访问数据库的保护,在发送完数据库消息后使用OsAcquireSemaphore( gDb2Sem, INFINITE )等待获取信号量,而gDb2Sem这个信号量是需要执行完数据库操作后才会被释放,这样就确保了当前消息队列里的消息必须被执行完(数据库操作执行完),才会执行下一个消息.
<2> hAceDBSingleMutex
互斥锁的使用是对数据库接口在非Mocha线程情况下需要访问数据库时进行保护的,这个保护是确保同一时间内只会有一个数据库操作的消息被送出,由于Mocha平台是多线程平台,所以APP在使用数据库是有可能在并行情况下使用,因此这个互斥锁的使用确保了不会在前一条消息发送完但还没有被执行完时,就发送写一条消息,严格确保了数据库访问的顺序性.
总结:对于AceDB和DB2的多线程和单线程的访问,基本方法如上所述,在SHP平台之前的版本中使用DB2数据库是采用的所线程访问,经过移植后目前SHP平台使用的AceDB采用的是单线程访问,经过对比,后者的综合性能要更好.
PS:
所以瓶颈在数据库,数据库端只能单线程处理插入,此时的多线程没有用处了。 这个例子也说明,并不是多线程就一定比单线程快。要看是否有合适的环境。 一般说来,有如下特点的环境可能适用多线程: 1)多CPU机器,或多核。比如压缩文件在单CPU机器上,多线程可能更慢。 2)需要做IO等待的任务。比如网络等待。例如:WEB服务器,多线程下载。 |