TADOQuery и TADOConnection утечка памяти

Я использую 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, у меня не возникает та же проблема.

2 ответа

Вы должны передать NULL в качестве владельца и удалить созданные объекты самостоятельно. Также опасно вызывать 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...
}
Другие вопросы по тегам