多线程动态生成TADOConnection和TADOQuery,多个客户端每隔50毫秒发送数据服务器出现out of memory错误?
我是多个客户端连接服务器端,服务器端收到客户端的请求后,到数据库里查询数据,然后返回给客户端,服务端用TServerSocket,方式为stNonBlocking,数据库连接用TADOConnection,查询用TADOQuery,数据库用的是sql server,建了一个100万的数据表,服务器从数据库里查询出来一个结果的时间是比较快。每个线程都动态生成一个数据库连接和一个TADOQuery。
我现在用6个客户端同时连接服务器端,然后每个客户端都每隔50毫秒就向服务器端发送请求,结果服务器老是出现 "EOutOfMemoyr out of memory "的错误,错误的地方有的是出现在新建TADOQuery的地方,有的时候出现在其他地方。
请各位指点一下,下面是源代码:
ProcessData这个过程是在多线程ExecuteData过程中调用执行的
//--------------------
void __fastcall TRecvStreamThread::ProcessData(AnsiString ReceiveText)
{
/*变量定义部分*/
......
TADOConnection *AdoConn;//数据库连接对象
TADOQuery *AdoMeth;//数据库操作对象
//初始化COM对象
CoInitialize(NULL);
try
{
try
{
//创建数据库连接对象
AdoConn=new TADOConnection(NULL);
//创建数据库操作对象
AdoMeth=new TADOQuery(NULL);
//创建是否成功,如果不成功,则退出
if (AdoConn==NULL || AdoMeth==NULL)
{
return;
}
//如果数据库连接字符串为空
//ThreadDataConnectString为一个全局的数据库连接字符串
if (ThreadDataConnectString== " ")
{
return;
}
AdoConn-> ConnectionString=ThreadDataConnectString;
AdoConn-> Connected=true;
AdoMeth-> Connection = AdoConn;
AdoMeth-> CursorLocation=clUseClient;
AdoMeth-> CursorType=ctStatic;
AdoMeth-> CacheSize=500;
AdoMeth-> Close();
AdoMeth-> SQL-> Clear();
//开始处理客户请求服务
........
//返回给客户端回复数据
send(TermScoket,Sendstr.c_str(),Sendstr.Length(),0);
}
catch(...)
{
}
}
__finally
{
try
{
//关闭数据库连接
if (!AdoConn==NULL)
{
if (!AdoMeth==NULL)
{
delete AdoMeth;
AdoMeth=NULL;
}
Sleep(5);
delete AdoConn;
AdoConn = NULL;
}
//销毁COM对象
CoUninitialize();
/*写日志*/
Main-> pLockX-> Acquire();//pLockX为一个全局临界区变量
//调用写日志函数
Main-> pLockX-> Release();
}
__finally
{
}
}
}
[解决办法]
这种写法有问题,很难保证你的sql在50毫秒内完成,而且每次执行都要new connection 和query,这样写每次都会分配新资源和数据库建立新连接,所以会出现你说的那个问题,建议只建立一个connection和query,然后连接好,然后调用query操作就ok了,没必要每次都new,然后close 和delete.
[解决办法]
Lurel说的非常正确!
补充一下我的看法:
你这种处理方法根本就没有什么多线程的优势!
简直就是晕!
采用这种架构!
使用一个全局ADOQuery
在线程里面进行 Parse你接受到的数据,然后加锁,进行ADOQuery操作
然后回发数据
如果发现ADOQuery的相应非常延迟的话
可以考虑专门用一个线程处理这个操作
避免响应时间不够
我的博客好像有一个类时的模型!你可以搜索一下!不过是使用UDPServer实现的。或许是TCPServer实现的
[解决办法]
如果发现ADOQuery的相应非常延迟的话
可以考虑专门用一个线程处理这个操作
你没有理解这句话的意思
还是你根本就没有认真看呢?
比如这样 将指令流放到一个vector中
然后
CS-> Enter();
//先获得vector中的所有要处理的sql
CS-> Leave()
执行新vector中的SQL
[解决办法]
》执行新vector中的SQL
这里使用但一ADOQuery来处理 就可以了
[解决办法]
try
{
Main-> pLockX-> Acquire();//鎖定與解鎖應放在 try 之外,注意發生異常後要能正確解鎖
//创建数据库连接对象
AdoConn=new TADOConnection(NULL); //應共用連接,而不是每次創建一個,這樣占用服務器資源大,速度反而慢。
//创建数据库操作对象
AdoMeth=new TADOQuery(NULL);//其實也可以共用一個TADOQuery的,如果只是發SQL語句到服務器,直接用ADOConnection發也好。
//创建是否成功,如果不成功,则退出
if (AdoConn==NULL || AdoMeth==NULL) //現時的C++,失敗時會產生異常,不再是返回NULL了。
{
return;
}
//如果覺得一個連接速度不夠好,可以排隊共用 3~4 個連接。過多的連接不會對速度有什麼幫助,只會加大服務器負擔。每次操作完成後就斷開,同樣會加大服務器負擔。
例如:
共用 3~4 個連接(查詢方式) :
class TShareADOConnection
{
TADOConnection *Connection[4] ;
bool Use[4] ;
public :
TShareADOConnection() {正確建立4個連接}
TADOConnection *GetShareADOConnection()//取得並占用一個可用的連接
{
while(1)
{
for(int i = 0 ; i < 4 ; ++i)
if( ! Use[i])
return Connection[i] ;
sleep(50)
}
}
void FreeShareADOConnection(TADOConnection *pConn)//釋放
{
for(int i = 0 ; i < 4 ; ++i)
if( Connection[i] == pConn)
{
Use[i] = false ;
break ;
}
}
};
用例:
AdoConn = ShareADOConnection.GetShareADOConnection();
....用戶操作
ShareADOConnection.FreeShareADOConnection(AdoConn );
共用 3~4 個連接(排隊方式) :這裡代碼量會比較多,指導思想是構造一個簡單虛擬機(就是一個自動執行機制),把想要執行的代碼(函數或SQL語句)交付給虛擬機執行,從而簡化編程。我只寫部分框架
typedef void (*TFun)(TADoConnection *conn) ; //函數指針,指向將要用線程執行的代碼
class TFunTask
{
public :
TFun Fun ;
int ThreadFlag ;
AnsiString SQL ; //Fun Data ;
};
class TVirtualRun
{
TMyThread Thread[4] ;
std::queue <TFunTask> TaskQueue; //任務隊列。
void ThreadRunTask(TFunTask *Task);
bool Running ;
public :
void AddFun(TFunTask Task) {
FunQueue.ThreadFlag = 0 ; //標識該任務未執行。
FunQueue.push_back(Task);
if( ! Running )
Run();
}
void Run()//虛擬機執行
{
while ( 1 )
{
bool Empty = true;
std::queue <TFunTask> ::iterator pos = FunQueue.begin();
while(pos != FunQueue.end())
{
if(pos -> ThreadFlag == -1 ) // 執行完成
FunQueue.erase(pos) ;
else if(pos -> ThreadFlag == 0 ) //未執行
{
ThreadRunTask(&*pos);
Empty = false ;
}
else ; //執行中。
}
if(Empty) break ;
}
}
void ThreadRunTask(TFunTask *Task)
{
//用自定義的四個線程池去執行任務。每個線程占用一個ADOConnection,注意是占用,而不是用完就釋放。
TMyThread *Thread = GetThread();
if(Thread)
{//Run .
Task.ThreadFlag = 1 ;
}
}
};
[解决办法]
并不是线程多就是执行快.
你应该建立一个环形先进先出的接收缓冲区,当收到的SOCKET请求时先保存到这个缓冲区中,
创建一个连接,一个ADOQuery,
用一个线程A从缓冲区的另一头如果有数据就去查.然后将结果放在另一个发送缓冲区中.线程B从发送缓冲区中读取数据发送给客户.
这样用两个线程,两个缓冲区就完成了.如果来的数据实在太快,可以调整缓冲区的大小.
但是如果数据还是太密集,就得用I/O完成端口的方式来接收数据了.
[解决办法]
应该在运行期间尽量少的创建和销毁对象,这是很费时间的.
线程过多,系统会忙于切换线程,真正用来工作的时间反而少了.
两个缓冲区的读写要用临界区来进行同步,在受保存的代码中仅读取数据,然后设置当前位置为空就可以,不要在受保护的代码段做费时的操作,否则线程的优势就没有了.
两个缓冲区最好是用数组来实现,这样一次就分配好了空间,不用在每次收数据的时候分配内存.
[解决办法]
50毫秒,未免过太于牵强了吧,一秒钟不是要发二十次??
[解决办法]
程序写的超级垃圾,怎么分割功能实在太烂了
[解决办法]
使用线程可以考虑使用线程池,使用连接考虑使用连接池,数据库操作要分类做继承或接口,你的程序也太难看了吧
[解决办法]
这个挺有意思的。关注。
[解决办法]
ZyxIp(绝望中...) 说的 和我的一样的意思
他分析的也比较好
[解决办法]
同意
写个先进先出队列,将收到指令放入队列,由另一处理线程处理,再进结果返回另一个队列,由收发线程发送出去。
[解决办法]
我猜测楼主真正的意思是测试服务器的压力
这个不用测试了,如果真的是并发用户,能顶住100个的服务器都要比较高级的了
想想看,上海证券交易所的交易处理能力是16000笔/秒,他们的服务器配置虽然不知道,但相信怎么也得是中型机级别的吧。
象你这个,6*1000/50=120,普通的PC服务器十有八九是内存溢出,这个和多线程关系不大,不管采用什么方式,连接数据库服务器是实实在在的。
实际使用中,100个客户端的CS系统并发用户数一般在20-40之间,你根本不用管这些。
[解决办法]
关注
[解决办法]
50豪秒很难保证数据的准确性~~一般采用udp or tcp
传送sql过去处理
[解决办法]
学习 收藏了!
[解决办法]
IOCP创建十来个线程,每个线程使用一个Connection,创建好,等待客户端
客户端来请求,IOCP会自动激活一个线程专门处理你的请求,速度非常快
其实用十来个线程WaitForMultiObject([hExitEvent,hSockEvent])也差不多效果
记住要创建好对象放在那里等,而不是来一个创建一个,而且要用队列的思想,避免同时创建的东西太多
[解决办法]
避免频繁创建和销毁对象
[解决办法]
Semaphore也可以用于同步
[解决办法]
mark