2013-10-19 31 views
0

我使用C++ Builder XE3。在Windows服务中,我们在函数tcp_serverExecute(TIdContext * AContext)上有(Indy TCP服务器)上的IdTCP服务器 - 根据我的理解产生新线程。TADOQuery和TADOConnection内存泄漏

创建TADOConnection和TADOQuery(后我调用CoInitialize) 问题是不管我,除非我使用服务对象作为父连接和查询

::CoInitialize(NULL); 
    TADOConnection * sql_conn = new TADOConnection(service_object); 
    TADOQuery * pos_q = new TADOQuery(service_object); 

try 
{ 

} 
__finally 
{ 
    delete pos_q; 
    delete sql_conn; 
    ::CoUninitialize(); 
} 

但是如果我使用你的应用程序总是出现内存泄漏服务对象作为父我最终得到一个异常和应用程序崩溃。如果我使用NULL作为父(所有者)工作就好,但进程在内存中不断增长。据我所知和测试,如果我在TThread做类似的代码,我不会得到同样的问题。

+0

为什么你认为有泄漏?进程内存的增加不能保证是真实泄漏的指示。请记住,RTL缓存并重新使用释放的内存,但不会返回到操作系统。你可能会看到内存碎片,而不是内存泄漏。如果您尚未安装,则应安装[FastMM](http://fastmm.sourrceforge.net)或其他设计用于防止碎片的内存管理器。 –

回答

0

你应该通过NULL所有者和自己删除创建的对象,同时调用CoInitialize和CoUninitialize内螺纹是危险的,把他们在形式的构造函数和析构函数:

TADOConnection * sql_conn = new TADOConnection(NULL); 
TADOQuery * pos_q = new TADOQuery(NULL); 

try 
{ 
} 
__finally 
{ 
    delete pos_q; 
    delete sql_conn; 
} 
+0

是的,你应该使用'NULL'所有者,但是在这种情况下'CoInitialize/Ex()'和'CoUniitialize()'必须被调用,因为这个代码在'TIdTCPServer'创建的工作线程中运行,所以调用它们在窗体的构造函数/析构函数中不是一个选项。 ADO使用COM对象,并且在创建和使用COM对象之前,任何想要访问COM对象的工作线程都必须调用CoIntialize/Ex()来建立线程与COM的关系(单元线程与多线程)。 –

0

COM只应初始化每个线程一次,但在客户的整个生命周期中,多次触发事件OnExecute

如果你不使用线程与TIdTCPServer池(通过附加一个TIdSchedulerOfThreadPool组件到TIdTCPServer::Scheduler属性),那么你可以使用TIdTCPServer::OnConnectTIdTCPServer::OnDisconnect事件来初始化/完成您的ADO对象,然后在TIdTCPServer::OnExecute事件作为使用它们需要,如:

class TMyContextData 
{ 
public: 
    TADOConnection *sql_conn; 
    TADOQuery *pos_q; 

    TMyContextData(); 
    ~TMyContextData(); 
}; 

TMyContextData::TMyContextData() 
{ 
    sql_conn = new TADOConnection(NULL); 
    pos_q = new TADOQuery(NULL); 
} 

TMyContextData::~TMyContextData() 
{ 
    delete pos_q; 
    delete sql_conn; 
} 

void __fastcall TMyForm::tcp_serverConnect(TIdContext *AContext) 
{ 
    ::CoInitialize(NULL); 
    AContext->Data = new TMyContextData; 
} 

void __fastcall TMyForm::tcp_serverDisconnect(TIdContext *AContext) 
{ 
    delete static_cast<TMyContextData*>(AContext->Data); 
    AContext->Data = NULL; 
    ::CoUninitialize(); 
} 

void __fastcall TMyForm::tcp_serverExecute(TIdContext *AContext) 
{ 
    TMyContextData *pData = static_cast<TMyContextData*>(AContext->Data); 
    // use pData->sql_conn and pData->pos_q as needed... 
} 

或者,从TIdServerContext派生一个新类来代替:

class TMyContext : public TIdServerContext 
{ 
public: 
    TADOConnection *sql_conn; 
    TADOQuery *pos_q; 

    __fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = NULL); 
    __fastcall ~TMyContext(); 
}; 

__fastcall TMyContext::TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList) 
    : TIdServerContext(AConnection, AYarn, AList) 
{ 
    ::CoInitialize(NULL); 
    sql_conn = new TADOConnection(NULL); 
    pos_q = new TADOQuery(NULL); 
} 

__fastcall TMyContext::~TMyContext() 
{ 
    delete pos_q; 
    delete sql_conn; 
    ::CoUninitialize(); 
} 

__fastcall TMyForm::TMyForm(TComponent *Owner) 
    : TForm(Owner) 
{ 
    // do this before activating TIdTCPServer 
    tcp_server->ContextClass = __classid(TMyContext); 
} 

void __fastcall TMyForm::tcp_serverExecute(TIdContext *AContext) 
{ 
    TMyContext *pContext = static_cast<TMyContext*>(AContext); 
    // use pContext->sql_conn and pContext->pos_q as needed... 
} 

但是,如果哟您正在使用线程池,那么多个客户端可以由相同的物理线程提供服务,因此您应该将您的COM初始化移动到管理TIdContext对象的实际线程对象(您还应该将ADO对象移动到线程中,以便可以重用它们对于多个客户),例如:

class TMyADOThread : public TIdThreadWithTask 
{ 
protected: 
    virtual void __fastcall AfterExecute(); 
    virtual void __fastcall BeforeExecute(); 

public: 
    TADOConnection *sql_conn; 
    TADOQuery *pos_q; 

    __fastcall TMyADOThread(TIdTask *ATask = NULL, const String AName = ""); 
}; 

__fastcall TMyADOThread::TMyADOThread(TIdTask *ATask, const String AName) 
    : TIdThreadWithTask(ATask, AName) 
{ 
} 

void __fastcall TMyADOThread::BeforeExecute() 
{ 
    TIdThreadWithTask::BeforeExecute(); 
    ::CoInitialize(NULL); 
    sql_conn = new TADOConnection(NULL); 
    pos_q = new TADOQuery(NULL); 
} 

void __fastcall TMyADOThread::AfterExecute() 
{ 
    delete pos_q; 
    delete sql_conn; 
    ::CoUninitialize(); 
    TIdThreadWithTask::AfterExecute(); 
} 

__fastcall TMyForm::TMyForm(TComponent *Owner) 
    : TForm(Owner) 
{ 
    // do this before activating TIdTCPServer 
    IdSchedulerOfThreadPool1->ThreadClass = __classid(TMyADOThread); 
} 

void __fastcall TMyForm::tcp_serverExecute(TIdContext *AContext) 
{ 
    TMyADOThread *pThread = static_cast<TMyADOThread*>(static_cast<TIdYarnOfThread*>(AContext->Yarn)->Thread); 
    // use pThread->sql_conn and pThread->pos_q as needed... 
}