2013-10-19 35 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... 
}