TIdTCPServer上下文中的应用程序挂起 - > LockList()

时间:2015-10-21 09:52:03

标签: c++ deadlock indy10

我正在C ++ Builder XE2中开发所谓的“馈送器”应用程序,它使用2个内置的Indy组件 - TIdTCPClient和TIdTCPSever。 TIdTCPClient用于接收来自一个源的数据,形成字符串消息,并且与使用TIdTCPSever相比,此字符串消息被发送到所有客户端应用程序。对于数据重新翻译,我使用下一个函数(idEventsServerSocket是TIdTCPSever组件):

void TfrmMainWindow::SendDataToAllClients(String msg) {

TList *ClientsList;
try
{
ClientsList = idEventsServerSocket->Contexts->LockList();


for (int i = 0; i < ClientsList->Count; i++) {
    TIdContext *Context = (TIdContext*)ClientsList->Items[i];


    bool connected = false;
    try {
        connected = Context->Connection->Connected();
    }
    catch (Exception&e) {

        continue;
    }

    if (!connected)
        continue;
    try {
        Context->Connection->IOHandler->WriteLn(msg);
        Context->Connection->IOHandler->WriteBufferFlush();
    }
    catch (Exception&e) {

    }

}
}
__finally
{
idEventsServerSocket->Contexts->UnlockList();
}


}

我还想注意这个函数包含在EnterCriticalSection ... LeaveCriticalSection代码部分中,因此应该保证在函数未被执行之前不会发生此函数代码的新入口。对于idEventsServerSocket OnException和OnListenException处理程序已定义并包含空代码。

所以问题是:有时是行

ClientsList = idEventsServerSocket->Contexts->LockList();

导致应用程序挂起。当它发生时没有一般规律,但看起来大多数情况下,当非常频繁地调用函数 SendDataToAllClients 时(例如每10-50毫秒一次)。客户端连接数从30到50不等。 我需要知道的是,有什么办法可以避免这种僵局吗?我有任何一种检查(如TryEnterCriticalSection)? 另外我想承认来自Delphi: TThreadList sometimes lock program的雷米的解决方案没有帮助。

1 个答案:

答案 0 :(得分:0)

LockList()必须位于try区块之外。

Contexts列表在内部使用关键部分,因此将此代码包装在您自己的关键部分中是多余的。

LockList()可以阻止的唯一方法是,如果另一个线程已经获得了锁并且没有释放它,或者因为它正忙于使用该列表,或者更可能是它崩溃并且没有释放锁。 / p>

请勿在此代码中致电Connected()WriteBufferFlush()。您没有使用写缓冲,并且从Connected()事件之外调用TIdTCPServer将导致连接的InpuBuffer上的竞争条件,干扰管理该连接的服务器线程,可能会导致崩溃,死锁,入站数据损坏等。只需单独调用Write(),如果套接字已断开连接,则抛出异常。

您所展示的是一般使用TIdTCPServer实施TCP广播的不安全方式。您应该实现每个客户端线程安全的出站队列,并让OnExecute事件处理实际写入:

#include <IdThreadSafe.hpp>

class TMyContext : public TIdServerContext
{
public:
    TIdThreadSafeStringList *Queue;
    bool HasMsgsInQueue;

    __fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = NULL)
        : TIdServerContext(AConnection, AYarn, AList)
    {
        Queue = new TIdThreadSafeStringList;
        HasMsgsInQueue = false;
    }

    __fastcall TMyContext()
    {
        delete Queue;
    }
};

__fastcall TfrmMainWindow::TfrmMainWindow(TComponent *Owner)
    : TForm(Owner)
{
    // set this before activating the server
    idEventsServerSocket->ContextClass = __classid(TMyContext);
}

void TfrmMainWindow::SendDataToAllClients(const String &msg)
{
    TList *ClientsList = idEventsServerSocket->Contexts->LockList();
    try
    {
        for (int i = 0; i < ClientsList->Count; ++i)
        {
            TMyContext *Context = (TMyContext*) ClientsList->Items[i];
            try
            {
                TStringList *Queue = Context->Queue->Lock();
                try
                {
                    Queue->Add(msg);
                    Context->HasMsgsInQueue = true;
                }
                __finally
                {
                    Context->Queue->Unlock();
                }
            }
            catch (const Exception &)
            {
            }
        }
    }
    __finally
    {
        idEventsServerSocket->Contexts->UnlockList();
    }
}

void __fastcall TfrmMainWindow::idEventsServerSocketExecute(TIdContext *AContext)
{
    TMyContext *ctx = (TMyContext*) AContext;
    if (ctx->HasMsgsInQueue)
    {
        TStringList *Msgs = NULL;
        try
        {
            TStringList *Queue = ctx->Queue->Lock();
            try
            {
                Msgs = new TStringList;
                Msgs->Assign(Queue);
                Queue->Clear();
                ctx->HasMsgsInQueue = false;
            }
            __finally
            {
                ctx->Queue->Unlock();
            }

            AContext->Connection->IOHandler->Write(Msgs);
        }
        __finally
        {
            delete Msgs;
        }
    }

    if (AContext->Connection->IOHandler->InputBufferIsEmpty())
    {
        AContext->Connection->IOHandler->CheckForDataOnSource(100);
        AContext->Connection->IOHandler->CheckForDisconnect();

        if (AContext->Connection->IOHandler->InputBufferIsEmpty())
            return;
    }

    // handle inbound data as needed...
}