0

我使用 C++ Builder XE3。在 Windows 服务中,我们在函数 tcp_serverExecute(TIdContext *AContext) 上有 IdTCP 服务器(Indy TCP Server)——据我所知,它会产生新线程。

我创建 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 中执行类似的代码,我不会遇到同样的问题。

4

2 回答 2

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...
}
于 2013-11-20T03:02:24.070 回答
0

您应该将NULL作为Owner传递并自己删除创建的对象,在线程内调用 CoInitialize 和 CoUninitialize 也是危险的,将它们放入构造函数和析构函数中:

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

try
{
}
__finally
{
    delete pos_q;
    delete sql_conn;
}
于 2013-10-20T17:04:15.723 回答