Я использую C ++ Builder XE3. В службе Windows у нас есть сервер IdTCP (Indy TCP Server) в функции tcp_serverExecute (TIdContext * AContext) — который, как я понимаю, порождает новый поток.
Я создаю 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, у меня не возникает та же проблема.
Вы должны пройти НОЛЬ как владелец и удалить созданные объекты самостоятельно. Также опасно вызывать CoInitialize и CoUninitialize внутри потока, помещать их в конструктор форм и деструктор:
TADOConnection * sql_conn = new TADOConnection(NULL);
TADOQuery * pos_q = new TADOQuery(NULL);
try
{
}
__finally
{
delete pos_q;
delete sql_conn;
}
COM должен быть инициализирован только один раз для потока, но OnExecute
событие запускается несколько раз в течение жизни клиента.
Если вы не используете пул потоков с TIdTCPServer
(прикрепив TIdSchedulerOfThreadPool
компонент к TIdTCPServer::Scheduler
собственность), то вы можете использовать TIdTCPServer::OnConnect
а также TIdTCPServer::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...
}