为什么多线程使用时老是出现空指针错误?附源代码如下

jiataizi 2007-05-21 05:24:14
我是多个客户端连接服务器端,服务器端收到客户端的请求后,到数据库里查询数据,然后返回给客户端,服务端用TServerSocket,方式为stNonBlocking,数据库连接用TADOConnection,查询用TADOQuery,数据库用的是sql server,建了一个100万的数据表,服务器从数据库里查询出来一个结果的时间是比较快,我用6个客户端同时连接服务器端,然后不断地发送6000个请求,老是出现以下错误:
1、eaccessviolation
2、Function call terminated by unhandled exception oxeedfade at address 0x7c812a5b
3、invaliation pointer operator
不知道怎么解决咯,请各位协助?
附我的源代码如下:

<--Frmmain.cpp-->//主窗体

//------------------------------------------------------------------------

void __fastcall TMain::ServerSocketClientError(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
ErrorCode = 0;
Socket->Close();
}
//------------------------------------------------------------------------
void __fastcall TMain::ServerSocketClientConnect(TObject *Sender,
TCustomWinSocket *Socket)
{
TRecvStreamThread *RecvThread;

try
{
//如果目前连接的数量大于设置的最大数量,则不接收该客户端的请求
//MaxThreadMaxNum:设置的最大允许连接数
//TotalThreadNum:当前的连接数

if (TotalThreadNum>MaxThreadMaxNum)
{
//关闭连接
Socket->Close();
return;
}
else
{
RecvThread = new TRecvStreamThread(true);
if (RecvThread==NULL)
{
MessageBoxA(NULL,"创建线程失败!","系统提示",0 );
return;
}
RecvThread->FreeOnTerminate = true; // 线程执行完毕自动释放资源
RecvThread->Priority = tpLower; // set the priority lower than normal
RecvThread->RemoteAddress=Socket->RemoteAddress ;
RecvThread->hWndNo =RecvThread->Handle;
RecvThread->TermScoket=Socket->SocketHandle;
RecvThread->Resume(); // 开始执行线程
}
}

catch(...)
{
MessageBoxA(NULL,"出现错误!","系统提示",0 );
if (!RecvThread==NULL)
{
RecvThread->Free();
TotalThreadNum--;
}
return;
}

}

<--Frmmain.h-->

//------------------------------------------------------------------------

#ifndef FrmMainH
#define FrmMainH
//------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Menus.hpp>
#include <ExtCtrls.hpp>
#include <ComCtrls.hpp>
#include <FileCtrl.hpp>
#include <ImgList.hpp>
#include <NMFtp.hpp>
#include <Psock.hpp>
#include <ScktComp.hpp>
#include <ToolWin.hpp>
#include <IniFiles.hpp>
#include <ComCtrls.hpp>
#include <NMFtp.hpp>
#include <Psock.hpp>
#include <ScktComp.hpp>
#include <Dialogs.hpp>

//------------------------------------------------------------------------
class TMain : public TForm
{
__published: // IDE-managed Components
TServerSocket *ServerSocket;
TTimer *Timer1;

TLabel *lblClientNum;
TLabel *Label1;
void __fastcall FormCreate(TObject *Sender);
void __fastcall ServerSocketClientConnect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ServerSocketClientError(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent,
int &ErrorCode);
void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift);
void __fastcall N3Click(TObject *Sender);
private: // User declarations
bool __fastcall Logo_Oper(void);
bool __fastcall ConnectDataBase();
long packHadle[50];
bool accessRight[50];
AnsiString shutdownkind;

int MaxThreadMaxNum;//最大允许连接数量

protected:
public: // User declarations
int ServerPort;
int TotalThreadNum;//当前连接总数量
AnsiString ServerAddress;
AnsiString DataConnectString;//数据库连接字符串
AnsiString ExeFilePath;//系统所在路径
__fastcall TMain(TComponent* Owner);
};
//------------------------------------------------------------------------
extern PACKAGE TMain *Main;
//------------------------------------------------------------------------
#endif

...全文
695 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
laowang2 2008-03-07
  • 打赏
  • 举报
回复
upup
uuussseeennn 2008-03-06
  • 打赏
  • 举报
回复
建议从下面介绍的原理去分析下你的代码:

1:通过线程创建函数,经常会把外部对象的指针通过参数传给新线程,而让其使用
2:外部对象所在的线程跟新线程不是同一个线程,所以拥有不同的栈,其元素的出栈入栈毫无关系
3:如果新线程使用的外部对象(在1中提到)在老线程中被释放,而新线程继续想使用,就会报出现空指针或访问内存违例。

findfriend 2008-03-03
  • 打赏
  • 举报
回复
dingyige!
coolcalf 2007-05-31
  • 打赏
  • 举报
回复
学习,帮顶
wanglovec 2007-05-30
  • 打赏
  • 举报
回复
季老大出马了 厉害 先 MARK


确实 多线程比较难搞
jiataizi 2007-05-22
  • 打赏
  • 举报
回复
谢谢季兄的指点,我已经把这个临界区修改成了全局变量了,在系统主窗体创建的时候创建这个临界区对象,在系统关闭的时候释放这个临界区。
还要麻烦季兄帮看一下其他的代码,有没有什么地方会出现错误。
jishiping 2007-05-21
  • 打赏
  • 举报
回复
太长了,没有工夫慢慢看。不过我看了前面的,发现一个问题:
_fastcall TRecvStreamThread::TRecvStreamThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
TCriticalSection *pLockX;
pLockX=new TCriticalSection;

//记录总线程的数量
pLockX->Acquire();
Sleep(5);
Main->TotalThreadNum++;
pLockX->Release();

delete pLockX ;
}
这儿使用的pLockX,没有任何作用。你的本意是阻止多个线程同时执行一段代码,但是你这样写,几个线程还是可以同时执行同一段代码。这儿的pLockX,应该写成全局或者某个cpp的静态变量。在任何TRecvStreamThread创建之前就需要new好,在不再使用TRecvStreamThread时才delete掉。
jiataizi 2007-05-21
  • 打赏
  • 举报
回复
<--RecvStream.h-->

//------------------------------------------------------------------------
#ifndef RecvStreamH
#define RecvStreamH
//------------------------------------------------------------------------
#include <vcl.h>
#include <Classes.hpp>
#include <IniFiles.hpp>
#include <ComCtrls.hpp>
#include <ADODB.hpp>
#include <winsock.h>
#include <StrUtils.hpp>


//------------------------------------------------------------------------
class TRecvStreamThread : public TThread
{
private:
/* -------------------------------------------------------------------
数据包接口结构
--------------------------------------------------------------------- */
typedef struct Receive_RECORD
{
AnsiString m_Size; //数据包大小(5位)
AnsiString m_Serv_Type; //服务类型
AnsiString m_Err_Number; //错误码
}
data_record_head;

void __fastcall ProcessData(AnsiString ReceiveText);
void __fastcall ExecuteData();
long nIndex;

protected:
void __fastcall Execute();

public:
__fastcall TRecvStreamThread(bool CreateSuspended);
AnsiString RemoteAddress;
SOCKET TermScoket;

unsigned int hWndNo; //本身的句柄,用来判断是否结束。
int SendLen;
__published:


};
//------------------------------------------------------------------------
#endif
jiataizi 2007-05-21
  • 打赏
  • 举报
回复
//--------------------------------------------------------
void __fastcall TRecvStreamThread::ProcessData(AnsiString ReceiveText)
{
/*变量定义部分*/
AnsiString Services[2]={"0000","0001"};//服务类型列表
AnsiString BodyInfo[10];//存储包体中的各参数内容
AnsiString RecBody;//存储包体
AnsiString OpDateStr;//当前日期,格式为yyyymmdd
AnsiString Sendstr;//返回给客户端的数据
AnsiString SQLString;//数据库操作的SQL语句
AnsiString ResultInfo;
data_record_head RcTxt;//接收到的数据结构
FILE *FpLog;//写日志
int ServeType;//服务类型对应的在服务类型列表中的序号
int i,ChPos,cmdnum;
unsigned short int len,actlen;

AnsiString TermDir;//日志文件路径
AnsiString ErrCode;//错误编码
AnsiString UpdateDataKind;//保存数据的结果 0:成功 1:失败
AnsiString CmdResult;//是否存在从数据包中解析出来的服务类型 0:存在 1:不存在
AnsiString ReceiveLog,SendLog;//接收到的数据日志信息和发送的数据日志信息

TADOConnection *AdoConn;//数据库连接对象
TADOQuery *AdoMeth;//数据库查询对象

try
{
//初始化COM对象
CoInitialize(NULL);
try
{
//获取客户请求数据包信息
RcTxt.m_Size=Trim(ReceiveText.SubString(3,4)); //数据包大小
RcTxt.m_Serv_Type=Trim(ReceiveText.SubString(7,4)); //服务类型
RcTxt.m_Err_Number=Trim(ReceiveText.SubString(11,2)); //错误码
RecBody=ReceiveText.SubString(13,ReceiveText.Length()-12)+char(9); //获取包体

if (RcTxt.m_Size==NULL || RcTxt.m_Serv_Type==NULL || RcTxt.m_Err_Number==NULL || RecBody==NULL )
{
//如果获取的上述数据有一个为空,则返回
return;
}

//初始化一些参数值
......

//获取服务类型对应的序号
for (i=0;i<cmdnum;i++)
if (Services[i]==RcTxt.m_Serv_Type)
{
ServeType=i;
CmdResult="0";
break;
}

//如果获取的服务类型不存在,则退出
if (CmdResult=="1")
{
return;
}

//创建数据库连接
AdoConn=new TADOConnection(NULL);
//创建是否成功,如果不成功,则退出
if (AdoConn==NULL)
{
//销毁COM对象
CoUninitialize();
return;
}

//如果数据库连接字符串为空
//数据库连接字符串是保存在主窗体的DataConnectString中
if (Main->DataConnectString==NULL)
{
delete AdoConn;
AdoConn = NULL;
//销毁COM对象
CoUninitialize();
return;
}

AdoConn->ConnectionString=Main->DataConnectString;
AdoConn->Connected=true;
//创建数据操作对象
AdoMeth=new TADOQuery(NULL);
//创建是否成功,如果不成功,则退出
if (AdoMeth==NULL)
{
AdoConn->Close();
AdoConn->Connected = false;
delete AdoConn;
AdoConn = NULL;
//销毁COM对象
CoUninitialize();
return;
}

AdoMeth->Connection = AdoConn;
//AdoMeth->Connection =DataSourceWindow->Database1 ;
AdoMeth->CursorLocation=clUseClient;

//把包体中的各参数数据记录到BodyInfo数组中
for (i=0;RecBody.Length()>0;i++)
{
ChPos=RecBody.Pos(char(9));
BodyInfo[i]=Trim(RecBody.SubString(1,ChPos-1));
RecBody.Delete(1,ChPos);
}

//获取当前日期
OpDateStr=DateToAnsiString(Date());

//如果日志文件夹不存在,则自动创建,日志文件夹以客户端的IP地址命名
TermDir= Main->ExeFilePath+"data\\"+RemoteAddress;
if (!DirectoryExists(TermDir))
CreateDir(TermDir);

//记录客户请求数据日志
if (ServeType!=24)
{
ReceiveLog=(Time().TimeString()+" 请求包:"+AnsiReplaceText(ReceiveText,char(26),"")+"\n");
}

AdoMeth->Close();
AdoMeth->SQL->Clear();

//开始处理客户请求服务
switch (ServeType)
{
case 0:
.......//具体业务处理
break;
case 1:
.......//具体业务处理
break;
}

//如果是数据库操作失败,则返回给客户端数据库操作失败的信息
if (UpdateDataKind=="1")
{
Sendstr[11]='6';
Sendstr[12]='4';
}

//计算要返回给客户端数据包的长度
RcTxt.m_Size=(IntToStr(Sendstr.Length())+" ").SubString(1,4);
for (int i=1;i<5;i++)
Sendstr[2+i]=RcTxt.m_Size[i];

//返回给客户端回复数据
send(TermScoket,Sendstr.c_str(),Sendstr.Length(),0);

//记录数据接收和发送日志
if (ServeType!=24 )
{
SendLog=(Time().TimeString() +" 回复包:"+AnsiReplaceText(Sendstr,char(26),"")+"\n");
FpLog = fopen((Main->ExeFilePath+"\\data\\"+ RemoteAddress+"\\"+OpDateStr +".txt").c_str(), "a+"); //记录日志
//判断文件是否打开失败
if (FpLog==NULL)
{
}
else
{
fprintf(FpLog,ReceiveLog.c_str());
fprintf(FpLog,SendLog.c_str());
}
fclose(FpLog);
delete FpLog;
}

}
catch(...)
{
}
}
__finally
{
try
{
//关闭数据库连接
if (!AdoMeth==NULL)
{
AdoMeth->Close();
delete AdoMeth;
AdoMeth=NULL;
}
Sleep(5);
if (!AdoConn==NULL)
{
AdoConn->Close();
AdoConn->Connected = false;
delete AdoConn;
AdoConn = NULL;
}
//销毁COM对象
CoUninitialize();
}
__finally
{
}
}

}
jiataizi 2007-05-21
  • 打赏
  • 举报
回复
<--RecvStream.cpp-->//多线程处理

//---------------------------------------------------------------------------
#include <vcl.h>
#include <dateutils.hpp>
#include <stdio.h>
#include <SysUtils.hpp>
#pragma hdrstop
#include "RecvStream.h"
#include "PublicVal.h"
#include "Socket.cpp"
#include "DataSourcewin.h"
#include "FrmMain.h"
#include "windows.h"
#include <iostream>
#pragma package(smart_init)

__fastcall TRecvStreamThread::TRecvStreamThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
TCriticalSection *pLockX;
pLockX=new TCriticalSection;

//记录总线程的数量
pLockX->Acquire();
Sleep(5);
Main->TotalThreadNum++;
pLockX->Release();

delete pLockX ;
}


//----------------------------------------------------------------------
void __fastcall TRecvStreamThread::ExecuteData()
{
char buf[1024];
int pktlen,m_pack_long;
AnsiString RsText,RcData;

try
{
try
{
m_pack_long=0 ;
RsText="";

do
{
Sleep(3);
Application->ProcessMessages();
pktlen = recv(TermScoket,buf,sizeof(buf),0);
m_pack_long++;
}
while (m_pack_long<1000 && pktlen<0);

if (pktlen<0)
{
//接收数据失败
return ;
}
else if (pktlen == 0)
{
//收到的数据为空字串 ,表示对方已经关闭连接
return;
}
else
{
//处理接收到的数据,验证是否为合法的数据

RsText= StrPas(buf);

//数据长度是否大于12位,数据头两位是否为10
if(pktlen > 12 && RsText.SubString(1,2)=="10")

{
RsText = AnsiString(buf).SubString(1,pktlen);

m_pack_long= StrToInt("0" +Trim(RsText.SubString(3,4)));
//如果数据包实际长度和数据包中的长度不一致
if (RsText.Length()!=m_pack_long && RsText.Length()<13)
{

}
else
{
RcData= AnsiReplaceText(RsText,char(26),"");
//执行处理数据业务
ProcessData(RcData);
}

}
}
}
catch(Exception &exception)
{
Application->ShowException(&exception);
}
}
__finally
{
try
{
delete buf;
}
__finally
{
}
}

}

1,316

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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