CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
不看会后悔的Windows XP之经验谈 简单快捷DIY实用家庭影院
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  VC/MFC >  数据库

使用 CDatabase、MySQL5.0.22数据库、MyODBC 3.51.12,句柄(handle)持续增长 的问题

楼主LoveTide(say no to ISO-8859-1、CVS)2006-07-03 14:56:49 在 VC/MFC / 数据库 提问

我在开发一个电信应用程序,该程序需要对呼叫进行详细记录,每个呼叫可能需要访问数据库(都是   INSERT、UPDATE)至少5次,我是用“短连接”的方式来访问数据库的,所谓“短连接”就是每个   SQL   语句都创建一个新的连接,用完了就关闭之。  
   
  我用   MySQL   5.0.22、ODBC   来连接数据,但是发现句柄数一直在增长,一晚上能长   10万左右,非常恐怖,我将程序的日志打开,发现每个连接都关闭了,对象也释放了,内存没有增长/泄露,但是句柄一直长,不知何故。  
   
  具体到代码实现,我是这样做的:  
  从   CDatabase   继承一个类:SQLTask,并重写   BindParameters   函数,每次需要访问数据库时,创建一个   SQLTask   对象,并放到一个列表中,然后发一个消息给   SQL   线程,SQL线程得到消息后,从列表中取出该   SQLTask,然后   OpenDatabase--->ExecuteSQL,执行完毕后,再发送一个消息到主线程,主线程收到该消息后把   SQLTask   释放掉  
   
   
  下面是我的部分代码:  
  //###################################   SQLTask.h   ###########################  
  //   MFC   数据库  
  #include   <afxdb.h>  
   
  #include   "Task.h"  
  #include   "SQLParameter.h"  
   
  class   SQLUser;  
   
  class   SQLTask   :   public   Task   ,   public   CDatabase    
  {  
  public:  
  static   SQLTask   *   GetAndRemoveFirstSQLTask   (void);  
  static   void   AddSQLTask   (SQLTask   *pSQLTask);  
  bool   OpenDatabase   ();  
  void   CloseDatabase   (void);  
  bool   DoExecuteSQL   ();  
   
  int   SetReturnParameter   (SQLParameter   *   pParam);  
  SQLParameter   *   GetReturnParameter   (void);  
  int   AddParameter   (SQLParameter   &   param);  
  int   AddParameter   (SQLParameter   *   pParam);  
  int   SetParameters   (CArray   <SQLParameter   *,   SQLParameter   *>   &   arrayInputOutputParameters);  
  int   AddParameters   (CArray   <SQLParameter   *,   SQLParameter   *>   &   arrayInputOutputParameters);  
  CArray   <SQLParameter   *,   SQLParameter   *>   &   GetInputOutputParameters   (void);  
   
  void   BindParameters   (HSTMT   hStmt);  
  SQLRETURN   GetSQLDiagnosticInformation_Record   (SQLSMALLINT   HandleType,   SQLHANDLE   hHandle,   SQLRETURN   returnCode);  
  //SQLRETURN   GetSQLDiagnosticInformation_Field   (SQLSMALLINT   HandleType,   SQLHANDLE   hHandle,   SQLRETURN   returnCode);  
   
  void   SetSQLUser   (SQLUser   *pSQLUser);  
  SQLUser   *   GetSQLUser   (void);  
  bool   IsTaskOver   (void);  
  SQLTask   &   operator   =(SQLTask   &originalSQLTask);  
  SQLTask   (SQLTask   &   originalSQLTask);  
  SQLTask   (  
  SQLUser   *pSQLUser,  
  const   char   *strConnectionString,  
  const   char   *strSQLStatement,  
  SQLParameter   *   pReturnParameter,  
  CArray<SQLParameter   *,   SQLParameter   *>   &   arrayIOParameters  
  );  
  SQLTask();  
  virtual   ~SQLTask();  
   
  protected:  
  //   连接字符串  
  char   m_strConnectionString[256];  
   
  //   SQL   语句  
  char   m_strSQLStatement[1024];  
   
  //   执行该   SQL   所需要的输入、输出参数(不包括返回参数)  
  CArray   <SQLParameter   *,   SQLParameter   *>   m_ArrayAllInputOutputParameters;  
   
  //   执行该   SQL   所需要返回参数  
  SQLParameter   *   m_pReturnParameter;  
   
  private:  
  void   FreeReturnParameter   (void);  
  void   FreeInputOutputParameters   (void);  
  void   FreeAllParameters   (void);  
   
  void   CopyProperties   (SQLTask   &originalSQLTask);  
  void   Init   (void);  
   
  SQLUser   *   m_pSQLUser;  
  };  
  问题点数:60、回复次数:5Top

1 楼LoveTide(say no to ISO-8859-1、CVS)回复于 2006-07-03 15:00:05 得分 0

void   SQLTask::BindParameters(HSTMT   hStmt)  
  {  
  logger.Log   (LL_TRACE,   "SQLTask   %s::BindParameters:   Binding...",   GetName());  
   
  int   i,   paramNO=0,   inputOutputParameterCount;  
  inputOutputParameterCount   =   m_ArrayAllInputOutputParameters.GetSize   ();  
  SQLRETURN   rc;  
  SQLParameter   *pParam;  
   
  if   (m_pReturnParameter)  
  { //   有返回参数  
  paramNO   ++; //   paramNO   =   1;  
   
  pParam   =   m_pReturnParameter;  
  rc   =   SQLBindParameter   (  
  hStmt, //   语句的   Handle  
  paramNO, //   参数编号,以   1   开始  
  pParam->GetParamType(), //   参数的输入输出类型  
  pParam->GetValueType(), //   C   语言的参数类型  
  pParam->GetParamDataType(), //   参数类型  
  pParam->GetColumnSize(), //   列宽  
  pParam->GetDecimalDigits(), //   小数位数  
  pParam->GetParamValue(), //   输出缓冲区/参数值的指针  
  pParam->GetBufferLength(), //   输出缓冲区的大小  
  pParam->GetValueLengthPointer() //   值的大小/字符串长度/数据长度等等  
  );  
   
  logger.Log   (LL_TRACE,   "\tBind   Parameter   %d   (%d:%-s):   return   %d",  
  paramNO,  
  pParam->GetParamType   (),  
  pParam->GetParamTypeDescription   (),  
  rc  
  );  
   
  ASSERT   (   (rc   ==   SQL_SUCCESS)   ||   (rc   ==   SQL_SUCCESS_WITH_INFO)   );  
  if   (   (rc   ==   SQL_ERROR)   ||   (rc   ==   SQL_SUCCESS_WITH_INFO))  
  {  
  GetSQLDiagnosticInformation_Record   (SQL_HANDLE_STMT,   hStmt,   rc);  
  }  
  }  
  else  
  { //   无返回参数  
  }  
   
  //  
  for   (i=0;   i<inputOutputParameterCount;   i++)  
  {  
  paramNO   ++;  
   
  pParam   =   m_ArrayAllInputOutputParameters[i];  
  rc   =   SQLBindParameter   (  
  hStmt, //   语句的   Handle  
  paramNO, //   参数编号,以   1   开始  
  pParam->GetParamType(), //   参数的输入输出类型  
  pParam->GetValueType(), //   C   语言的参数类型  
  pParam->GetParamDataType(), //   参数类型  
  pParam->GetColumnSize(), //   列宽  
  pParam->GetDecimalDigits(), //   小数位数  
  pParam->GetParamValue(), //   输出缓冲区/参数值的指针  
  pParam->GetBufferLength(), //   输出缓冲区的大小  
  pParam->GetValueLengthPointer() //   值的大小/字符串长度/数据长度等等  
  );  
   
  logger.Log   (LL_TRACE,   "\tBind   Parameter   %d   (%d:%-s:%s):   return   %d",  
  paramNO,  
  pParam->GetParamType   (),  
  pParam->GetParamTypeDescription   (),  
  (  
  ((pParam->GetParamType   ()   ==   SQL_PARAM_INPUT)   ||   (pParam->GetParamType   ()   ==   SQL_PARAM_INPUT_OUTPUT))   ?   pParam->GetParamValue()   :   ""  
  ),  
  rc  
  );  
   
  ASSERT   (   (rc   ==   SQL_SUCCESS)   ||   (rc   ==   SQL_SUCCESS_WITH_INFO)   );  
  if   (   (rc   ==   SQL_ERROR)   ||   (rc   ==   SQL_SUCCESS_WITH_INFO))  
  {  
  GetSQLDiagnosticInformation_Record   (SQL_HANDLE_STMT,   hStmt,   rc);  
  }  
  }  
   
  logger.Log   (LL_TRACE,   "SQLTask   %s::BindParameters:   Bind   DONE   !",   GetName());  
  }  
   
   
   
  /*  
  描述:  
  打开数据库/建立数据库连接  
  (该   SQL   操作是在   SQL   线程中执行的)  
  说明:  
  */  
  bool   SQLTask::OpenDatabase()  
  {  
  bool   rc   =   false;  
  BOOL   rc2;  
   
  try  
  {  
  if   (CDatabase::IsOpen   ()) //   add   by   LiuYan;   2006.06.27;   如果已经打开,则不要重复打开之,否则可能造成句柄增加  
  {  
  logger.Log   (LL_INFO,   "SQLTask::OpenDatabase:   Database   (%s)   is   already   opened.",   m_strConnectionString);  
  rc   =   true;  
  }  
  else  
  {  
  logger.Log   (LL_DEBUG,   "SQLTask::OpenDatabase:   Opening   database   (%s)...",   m_strConnectionString);  
  //SetLoginTimeout   (5); //   time   out  
  //SetQueryTimeout   (10); //   time   out  
  rc2   =   CDatabase::OpenEx   (m_strConnectionString,   CDatabase::noOdbcDialog);  
  //   Nonzero   if   the   connection   is   successfully   made;  
  // otherwise   0   if   the   user   chooses   Cancel   when   presented   a   dialog   box   asking   for   more   connection   information.  
  //   In   all   other   cases,   the   framework   throws   an   exception.  
  if   (!   rc2)  
  {  
  logger.Log   (LL_WARNING,   "SQLTask::OpenDatabase:   Open   database   failed   !   ConnectionString=%s",   m_strConnectionString);  
  rc   =   false;  
  return   rc;  
  }  
  else  
  {  
  rc   =   true;  
  logger.Log   (LL_DEBUG,   "SQLTask::OpenDatabase:   Open   database   DONE   !");  
  }  
  }  
  }  
  catch   (CDBException   *pException)  
  {  
  //pException->GetErrorMessage   ();  
  pException->m_nRetCode;  
  pException->m_strError;  
  pException->m_strStateNativeOrigin;  
   
  logger.Log   (LL_WARNING,   "SQLTask::OpenDatabase:   CDBException   occur!   ConnectionString=\n%s\nSQL=\n%s\nRetCode=%d,   Error=%s,   StateNativeOrigin=%s",  
  m_strConnectionString,  
  m_strSQLStatement,  
  pException->m_nRetCode,  
  pException->m_strError,  
  pException->m_strStateNativeOrigin  
  );  
   
  pException->Delete   ();  
  rc   =   false;  
  return   rc;  
  }  
  catch   (CMemoryException   *pException)  
  {  
  //pException->GetErrorMessage   (  
  TCHAR   strError[255];  
  pException->GetErrorMessage   (strError,   sizeof(strError));  
  logger.Log   (LL_WARNING,   "SQLTask::OpenDatabase:   CMemoryException   occur   !   ConnectionString=\n%s\nSQL=\n%s\nError=%s",  
  m_strConnectionString,  
  m_strSQLStatement,  
  strError  
  );  
   
  pException->Delete   ();  
  rc   =   false;  
  return   rc;  
  }  
  catch   (...)  
  {  
  logger.Log   (LL_WARNING,   "SQLTask::OpenDatabase:   Exeception   occur   !   ConnectionString=\n%s\nSQL=\n%s",  
  m_strConnectionString,  
  m_strSQLStatement  
  );  
   
  rc   =   false;  
  return   rc;  
  }  
   
  return   rc;  
  }  
   
   
  void   SQLTask::CloseDatabase()  
  {  
  if   (CDatabase::IsOpen())  
  {  
  logger.Log   (LL_TRACE,   "SQLTask   %s::CloseDatabase:   Closing...",   GetName());  
  CDatabase::Close   ();  
  }  
  }  
   
   
  /*  
  描述:  
  执行   SQL   操作  
  (该   SQL   操作是在   SQL   线程中执行的)  
  说明:  
  */  
  bool   SQLTask::DoExecuteSQL()  
  {  
  bool   rc   =   false;  
  try  
  {  
  if   (CDatabase::IsOpen())  
  {  
  logger.Log   (LL_DEBUG,   "SQLTask::DoExecuteSQL:   Executing   SQL   (%s)...",   m_strSQLStatement);  
  CDatabase::ExecuteSQL   (m_strSQLStatement);  
  logger.Log   (LL_DEBUG,   "SQLTask::DoExecuteSQL:   Executing   SQL   DONE   !");  
   
  //   设置标志为“成功”  
  m_bTaskSuccessful   =   true;  
  rc   =   true;  
  }  
  else  
  {  
  logger.Log   (LL_WARNING,   "SQLTask::DoExecuteSQL:   Database   is   not   opened   !");  
  }  
  }  
  catch   (CDBException   *pException)  
  {  
  //pException->GetErrorMessage   ();  
  pException->m_nRetCode;  
  pException->m_strError;  
  pException->m_strStateNativeOrigin;  
   
  logger.Log   (LL_WARNING,   "SQLTask::DoExecuteSQL:   CDBException   occur!   ConnectionString=\n%s\nSQL=\n%s\nRetCode=%d,   Error=%s,   StateNativeOrigin=%s",  
  m_strConnectionString,  
  m_strSQLStatement,  
  pException->m_nRetCode,  
  pException->m_strError,  
  pException->m_strStateNativeOrigin  
  );  
   
  pException->Delete   ();  
  rc   =   false;  
  return   rc;  
  }  
  catch   (CException   *pException)  
  {  
  //pException->GetErrorMessage   (  
  TCHAR   strError[255];  
  pException->GetErrorMessage   (strError,   sizeof(strError));  
  logger.Log   (LL_WARNING,   "SQLTask::DoExecuteSQL:   CException   occur   !   ConnectionString=\n%s\nSQL=\n%s\nError=%s",  
  m_strConnectionString,  
  m_strSQLStatement,  
  strError  
  );  
   
  pException->Delete   ();  
  rc   =   false;  
  return   rc;  
  }  
  catch   (...)  
  {  
  logger.Log   (LL_WARNING,   "SQLTask::DoExecuteSQL:   Exeception   occur   !   ConnectionString=\n%s\nSQL=\n%s",  
  m_strConnectionString,  
  m_strSQLStatement  
  );  
   
  rc   =   false;  
  return   rc;  
  }  
   
  return   rc;  
  }  
  Top

2 楼LoveTide(say no to ISO-8859-1、CVS)回复于 2006-07-03 15:00:39 得分 0

//   ################################   主线程生成   SQLTask、发送消息给   SQL   线程   ################  
  BOOL   SQLUser::RequestExecuteSQL()  
  {  
  BOOL   rc   =   FALSE;  
  DWORD   errorCode;  
   
  m_pCurrentSQLTask   =    
  new   SQLTask   (  
  this,  
  m_strConnectionString,  
  m_strSQLStatement,  
  m_pReturnParameter,  
  m_ArrayAllInputOutputParameters  
  );  
   
  if   (m_pCurrentSQLTask)  
  {  
  SQLTask::AddSQLTask   (m_pCurrentSQLTask);  
   
  rc   =   PostThreadMessage   (  
  theApp.GetSQLThreadId(),  
  WM_BEGIN_EXECUTE_SQL,  
  0,  
  (LPARAM)this  
  );  
  }  
  else  
  {  
  }  
   
   
  logger.Log   (LL_TRACE,   "SQLUser::RequestExecuteSQL:   PostThreadMessage   return   %d.   (ThreadID=%d,   SQLThreadID=%d)",   rc,   GetCurrentThreadId   (),   theApp.GetSQLThreadId());  
   
  if   (!   rc)  
  {  
  errorCode   =   GetLastError   ();  
  }  
  ASSERT   (rc);  
  if   (!   rc)  
  {  
  logger.Log   (LL_ERROR,   "SQLUser::RequestExecuteSQL:   PostThreadMessage   failed!   ErrorCode=%u,   (ThreadID=%d,   SQLThreadID=%d)",   errorCode,   GetCurrentThreadId   (),   theApp.GetSQLThreadId());  
   
  OnSQLTaskOver   ();  
  }  
   
  return   rc;  
  }  
  Top

3 楼LoveTide(say no to ISO-8859-1、CVS)回复于 2006-07-03 15:00:52 得分 0

//   ####################################   SQL   线程处理   SQLTask   ##############################  
  DWORD   WINAPI   SQLThreadProc   (LPVOID   lpParam)  
  {  
  SQLTask   *pSQLTask   =   NULL;  
  //SQLUser   *pSQLUser   =   NULL;  
  BOOL   rc;  
  MSG   msg;  
   
  bool   bOpened;  
   
  //   In   the   thread   to   which   the   message   will   be   posted,    
  //         call   PeekMessage(&msg,   NULL,   WM_USER,   WM_USER,   PM_NOREMOVE)  
  //         to   force   the   system   to   create   the   message   queue  
  PeekMessage   (&msg,   NULL,   WM_USER,   WM_USER,   PM_NOREMOVE);  
   
  //   Set   the   event,   to   indicate   that   the   thread   is   ready   to   receive   posted   messages  
  rc   =   SetEvent   (theApp.GetSQLEvent()); //   激活主线程的   WaitForSingleObject   ()  
  ASSERT   (rc);  
   
  while   (true)  
  {  
  rc   =   GetMessage   (&msg,   NULL,   0,   0);  
  if   (rc   ==   0) //   If   the   function   retrieves   the   WM_QUIT   message,   the   return   value   is   zero.    
  { //   退出  
  break;  
  }  
  else   if   (rc   ==   -1)  
  { //   出现严重的程序错误  
  rc   =   GetLastError();  
  return   1;  
  }  
   
  //  
  if   (msg.message   ==   WM_BEGIN_EXECUTE_SQL)  
  {  
   
  logger.Log   (LL_TRACE,   "SQLThreadProc(ThreadID=%u),   WM_BEGIN_EXECUTE_SQL:   Executing   SQL...",   GetCurrentThreadId());  
   
  //   处理   SQL  
   
  while   (!   g_listAllPendingSQLTasks.IsEmpty())  
  {  
  pSQLTask   =   SQLTask::GetAndRemoveFirstSQLTask   ();  
  if   (pSQLTask)  
  {  
  if   (!pSQLTask->IsCancelled())  
  {  
  bOpened   =   pSQLTask->OpenDatabase   ();  
  if   (!pSQLTask->IsCancelled()) //   因为打开数据库需要一定的时间,所以,为了防止主线程取消这个   SQL   任务而不执行必要的操作时,就需要再判断一下……  
  {  
  if   (bOpened)  
  { //   打开数据库成功  
  pSQLTask->DoExecuteSQL   ();  
  }  
  else  
  { //   打开数据库失败  
  }  
   
  if   (!pSQLTask->IsCancelled()) //   因为执行   SQL   需要一定的时间,所以,为了防止主线程取消这个   SQL   任务而不执行必要的操作时,就需要再判断一下……  
  {  
  //   通知主线程:执行结束。(主线程需要判断执行的结果)  
  rc   =   eventSinkWindow.PostMessage   (WM_EXECUTE_SQL_ENDED,   0,   (long)pSQLTask);  
  ASSERT   (rc);  
   
  continue; //   重要!!!一定要,否则   SQL   任务将被视作已经被取消  
  }  
  else  
  {  
  logger.Log   (LL_DEBUG,   "SQLThreadProc:   SQLTask   %s   is   cancelled   after   DoExecuteSQL()",   pSQLTask->GetName());  
  }  
  }  
  else  
  {  
  logger.Log   (LL_DEBUG,   "SQLThreadProc:   SQLTask   %s   is   cancelled   after   OpenDatabase()   and   before   DoExecuteSQL()",   pSQLTask->GetName());  
  }  
  }  
  else  
  {  
  logger.Log   (LL_DEBUG,   "SQLThreadProc:   SQLTask   %s   is   cancelled   before   OpenDatabase()",   pSQLTask->GetName());  
  }  
   
  //   SQLTask   已经被取消,删除之……  
  ASSERT   (pSQLTask->IsCancelled());  
  delete   pSQLTask;  
  }  
  else  
  {  
  ASSERT   (false);  
  }  
  }  
  }  
  else   if   ((msg.message   ==   0)   ||   (msg.message   ==   WM_QUIT))  
  { //   退出  
  break;  
  }  
  }  
   
  return   0;  
  }  
  Top

4 楼vcnewer(磨剑)回复于 2006-09-27 17:36:42 得分 30

句柄增长   ?  
  有什么问题   ?Top

5 楼pioneer_public()回复于 2006-09-29 11:21:48 得分 30

问问题不要发这么长的代码  
   
  没有几个人可以看完  
   
  你只需要问关键的问题就可以了  
   
  我建议你可以将这个句柄发送到线程中,  
   
  使用后直接在线程中释放Top

相关问题

关键词

得分解答快速导航

  • 帖主:LoveTide
  • vcnewer
  • pioneer_public

相关链接

  • Visual C++类图书
  • Visual C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
提问
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo