奇怪!请教高手,多线程与ADO访问数据库的问题。

sxbyl 2000-10-28 11:37:00
问题是这样的,我在写一个服务器程序,有一个功能是将一些数据包中的数据解到数据库中,在这里我用了多先程,一个线程解一个包,线程最大数设为5,但有时会出问题,加入调试信息后发现是停到一个RecordSet的Open上,
ptrRds->PutRefActiveConnection(pWnd->m_ptrConn);
//此处有TRACE语句
hr=ptrRds->Open((_variant_t)bstrQuery,vNull, adOpenDynamic,adLockOptimistic,adCmdText);
我用SQL Server Profiler查看,发现这个Open根本没有得到执行,而且也没有死锁发生,程序也不占用CPU资源,只是没有任何响应。这个问题的发生也比较随机,有时解几个就死了,有时解几十个都没问题。
初始化我用的是
CoInitializeEx(NULL,COINIT_MULTITHREADED);
如果我把最大线程数设为1,则没问题。
...全文
709 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
halbert 2000-11-21
  • 打赏
  • 举报
回复
关于vc++ 的ado 编程
atdafx.h
#import "c:\program files\common files\system\ado\msado15.dll" no_namespace rename ("EOF", "adoEOF")

BOOL CGetemailApp::InitInstance()
{
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox("ole 出错");
return FALSE;
}
::CoInitialize( NULL );
}
int CGetemailApp::ExitInstance()
{
// TODO: Add your specialized code here and/or call the base class
::CoUninitialize();
return CWinApp::ExitInstance();
}

bool getrecordset()
{
char SqlStr[255],account[50];
char Error[500];
char putfilestr[300];
long recordnum;
m_pConnection = NULL;
m_pRecordset = NULL;
_variant_t TheEmail,ThePhone_office,TheAccount,TheUser_id;
char strEmail[50],strPhone_offiece[50],strAccount[50],strUser_id[50];
if(!OpenFileForReadWrite())
return ;
try{
m_pConnection.CreateInstance(__uuidof(Connection));
m_pRecordset.CreateInstance(__uuidof(Recordset));
recordnum=m_pConnection->Open("DSN=stat","test","test",-1);
sprintf(putfilestr,"%20s %30s %40s %30s\n","用户编码","用户帐号","用户Email","用户办公电话");
fwrite(putfilestr,strlen(putfilestr),1,fpout);
while(!feof(fpin))
{
fscanf(fpin,"%s",account);
sprintf(SqlStr,"select a.user_id,a.account, b.email,b.phone_office from userbasicinfo a,usercontactinfo b where a.account='%s' and b.user_id=a.user_id",account);
recordnum=m_pRecordset->Open(SqlStr,m_pConnection.GetInterfacePtr(),adOpenKeyset,adLockOptimistic,adCmdText);
recordnum=m_pRecordset->adoEOF;
//执行到此出错,为何。跳到catch(...)
if(!m_pRecordset->adoEOF)
{
TheUser_id=m_pRecordset->GetCollect("user_id");

TheAccount=m_pRecordset->GetCollect("account");
TheEmail=m_pRecordset->GetCollect("email");
ThePhone_office=m_pRecordset->GetCollect("phone_office");
sprintf(putfilestr,"%20s %30s %40s %30s\n",TheUser_id.bstrVal,TheAccount.bstrVal,TheEmail.bstrVal,ThePhone_office.bstrVal);
fwrite(putfilestr,strlen(putfilestr),1,fpout);
}//end if
m_pRecordset->Close();
}//end while
}//end try
catch(_com_error *e)
{

//CString Error = e->ErrorMessage();
//char Error[500];
strcpy(Error , (char*)e->Description());
AfxMessageBox(e->ErrorMessage());
}
catch(_com_error e)
{

//CString Error = e->ErrorMessage();
strcpy(Error , (char*)e.Description());
//AfxMessageBox(e->ErrorMessage());
AfxMessageBox(Error);
}
catch(...)
{
AfxMessageBox("ado 出错");
}

m_pRecordset->close();;
m_pConnection->Close();
m_pConnection->Release();
CloseAllFile();
}
// 执行到recordnum=m_pRecordset->adoEOF; 出错 为何
// 请那位大侠指点

sxbyl 2000-11-08
  • 打赏
  • 举报
回复
算了,这个问题就当解决了吧:(
sxbyl 2000-11-05
  • 打赏
  • 举报
回复
因为期限将至,为了下个月有饭吃,我先用一个投机的方法解决了,我将异常捕获后,启动一个新的线程来完成解包工作,到现在为止工作很好。另外,我发现一个很奇怪的问题,就是前面提到的那个“死锁”问题,如果程序运行期间不运行SQL的Enterprise Manager,就不会出现问题,如果运行这个东西,就包不准什么时候“死锁”了,虽然出现得很少。难道是adLockOptimistic用得不对?还有,前面提到的那个PostMessage到底是怎么回事?
zhq2000 2000-11-04
  • 打赏
  • 举报
回复
分数不能说明问题,又不能当饭吃!重要的是彼此互相学习,共同进步!

^_^

包含头文件:
#include <adoid.h>
*****************
CoMarshalInterThreadInterfaceInStream(IID_IADOConnection,m_pWnd->m_ptrConn,&pStream);
sxbyl 2000-11-03
  • 打赏
  • 举报
回复
各位老大,快帮小弟一把,这个问题应该不是特别困难吧,况且分值600啊!即使各位大侠视分数为粪土,但也应该路见不平,拔刀相助才是啊!拜托了!
sxbyl 2000-11-02
  • 打赏
  • 举报
回复
还是不行:( 而且虽然微软声称SQL有连接池,但我发现每个线程建立一个连接后速度明显下降,倒不是说建立连接慢,而是说数据库访问慢,远不及多个线程用一个连接时的速度,但出错频率低了些(还是原题中那个错误)。
sxbyl 2000-11-02
  • 打赏
  • 举报
回复
小弟另有一事相问,线程中使用PostMessage真的很安全吗?为什么我在线程中使用PostMessage有时会出现异常?即使将消息接收函数中的代码全部注释起来都不行,但只要将消息映射去掉就可以了,这是怎么回事?我被逼无奈,只好线程中直接调用消息处理函数,在函数中使用排斥,这样就没问题了。难道PostMessage的调用也需要排队?????
sxbyl 2000-11-02
  • 打赏
  • 举报
回复
这个连接池我倒也知道,我用性能分析其实也确实发现了这个东西,只是我们那位老大有这个要求,而且现在已经搞成这样了,就不太想改了。那我现在再改回去试试。希望能搞定,否则我真要崩溃了!!!!!!!!!!
sxbyl 2000-11-02
  • 打赏
  • 举报
回复
:( 还是不行,我执行CoMarshalInterThreadInterfaceInStream(__uuidof(Connection),m_pWnd->m_ptrConn,&pStream);出错,描述为“不支持此接口”,这个函数的调用者是一个在主对话框中创建的类。
sxbyl 2000-11-02
  • 打赏
  • 举报
回复
MSDN上是这么说的:CoInitializeEx is called only once by each apartment in the process that uses the COM library. For a multithread apartment, one call is sufficient for all threads in the apartment. 也就是说,只需要在程序初始化时使用一次COINIT_MULTITHREADED参数的CoInitializeEx即可,而且实际运行时也没有发现COM对象建立失败的现象,所以我觉得问题可能不在这里。
我是这样的:我的程序是一个基于对话框的程序,m_ptrConn是对话框中的protected型变量,所有的线程都是对话框类的友员,当传递参数时,我将对话框指针作为参数传递给线程。我现在先试一下你的方法,先谢过!
zhq2000 2000-11-02
  • 打赏
  • 举报
回复
共用一个连接时,所有的请求是串行的,所以对于一正在使用该连接的线程来说是要快点;
使用连接池时,所有的请求都并行的,它的速度取决于服务器的性能。

如果你要用一个连接,是否是将该传给其它的数据处理线程,如是,在MTA模式下,不能直接传送接口,必须用两个函数实现:
CoMarshalInterThreadInterfaceInStream()
及 CoGetInterfaceAndReleaseStream();

每一个线程都必须调用 CoInitializeEx;

在创建连接的线程中:

--------------------------------------------
//编组接口
IStream *pStream;
CoMarshalInterThreadInterfaceInStream(IID_IConnection,pConnection,&pStream);
//向数据处理线程传送接口
DWORD dwID;
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadRoutine,pStream,0,&threadId);
--------------------------------------

void __stdcall ThreadRoutine(IStream *pStream)
{
HRESULT hr;
hr=CoInitializeEx(NULL,COINIT_APARTMENTTHREADED) //or COINIT_MULTITHREAD
IConnection * pCnn;
hr=CoGetInterfaceAndReleaseStream(pStream,IID_IConnection,(void **)&pCnn);

//processing.....
.....

//last step , release the interface when you finished the work.
pCnn->Release();
}
----------------------------------------------------

Connection 接口我可能写得不太正确,你自己看看吧。



sxbyl 2000-11-01
  • 打赏
  • 举报
回复
现在我在进行整体测试时(一边上传数据,一边解包,但不是同一条记录对应的包),那个鬼问题又出现了,用了临界区还是不行,而且每次都是停在同一条语句。这个问题好像确实太麻烦了,所以我再加50分。
sxbyl 2000-11-01
  • 打赏
  • 举报
回复
唉!如果Open()能返回东西就好了,现在的问题是它根本什么都不返回,只是停到那条语句上(可以可见当时为了查这条改死的语句我费了多少精力)
zhq2000 2000-11-01
  • 打赏
  • 举报
回复
兄弟!不知我说的对不对!

是这样,虽然ADO支持多线程,但是一个数据库连接在一个时间内只能为一个请求服务,多个请求就需要排队啦;
还有,支持多线程的意思是:ADO组件可能被多个线程实例化、使用,互不干扰(线程安全)。不是一个数据库连接同时处理来自多个线程的请求,这需要排队处理,一个一个的来。

建议:一个线程独占一个数据库连接(用同一账号),嘿,不用认为这样做是在浪费资源,其实系统有个数据库连接池,系统并不是你一申请连接就马上给你建立一个新连接,而是检查连接池中有无闲置的连接,有则直接将它给你(省时),如没有才为你建立新连接。当你不需要时,系统也不是直接释放,而是放入连接池,如果过了一定时间不用或资源紧张才释放。

只有通过同一账号才能共享连接。

sxbyl 2000-11-01
  • 打赏
  • 举报
回复
再次加分!!!!
又出现一个要命的问题,是一个异常,具体描述如下:
Code=80040e21
Code meaningIDispatch error #3105
Source=Microsoft OLE DB Provider for ODBC Drivers
Description=多步OLE DB 操作产生错误。请检查每个OLE DB状态值。没有工作被完成。

哪位前辈知道这个鬼东西到底是怎么来的?什么意思?如果谁能解决我的难题,另外加300分给他。
zzh 2000-10-31
  • 打赏
  • 举报
回复
你判断一下Open()函数的返回码,如果出错,查一下错误码的意思,再找相应的解办法决
sxbyl 2000-10-31
  • 打赏
  • 举报
回复
我现在虽然问题解决了,但解决得不太明白,这是SQL的问题吗?这叫死锁吗?如果是死锁,为什么性能分析器没有报告?
如果没人发言,我只好加分了。
zzh 2000-10-29
  • 打赏
  • 举报
回复
你在使用多线程的时候可以通过使用临界量来控制同步问题,这样应该就可以了。
sxbyl 2000-10-28
  • 打赏
  • 举报
回复
我在操作的前面加了一行Sleep(DelayTime())也没问题了(DelayTime是一个字定的产生随机时间的函数),ODBC不会这么弱吧?难道它支持多线程这么差?微软可是大力鼓吹SQL的,如果说连这么简单的线程问题他都解决不好,好像是不大可能,但现在从现象上看确实如此,但为什么性能分析器没有查到死锁呢?
BenjaminNing 2000-10-28
  • 打赏
  • 举报
回复
用临界区方法!
.....
extern CCriticalSection csDao; //for Jet
.....
csDao.Lock();

CFrPosSet rsPos(&gdb);
CFrConfigSet rsParam(&gdb);

rsPos.m_strFilter.Format("iVPosAddr=%d", m_dcsBuff.msgHead.ucPosNo);

try
{
rsPos.Open(dbOpenSnapshot);
if (!rsPos.IsEOF())
m_cPosID = rsPos.m_cPOSID;
else
wErrorCode = PERR0_VPOS_NUM;
rsPos.Close();

rsParam.Open(dbOpenSnapshot);
m_cStoreID = rsParam.m_cStoreID;
m_cPCID = rsParam.m_cHostID;
m_iTrimPosion = rsParam.m_iTrimPosion;
m_bMSyn = rsParam.m_bMSyn;

rsParam.Close();
}
catch(CDaoException* e)
{
e->ReportError();
e->Delete();
wErrorCode = DAO_OPEN_ERROR;
}

csDao.Unlock();

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧