Зависание приложения на TIdTCPServer Contexts-> LockList ()

Я разрабатываю так называемое приложение «фидер» на C ++ Builder XE2, в котором используются 2 встроенных компонента Indy — TIdTCPClient и TIdTCPSever. TIdTCPClient используется для получения данных из одного источника, формирует строковое сообщение, а затем с использованием TIdTCPS всякий раз, когда это строковое сообщение отправляется всем клиентским приложениям. Для ретрансляции данных я использую следующую функцию (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 иногда блокирует программу не помогает.

0

Решение

LockList() должен быть за пределами try блок.

Contexts list внутренне использует критический раздел, поэтому перенос этого кода в собственный критический раздел является излишним.

Единственный способ LockList() Блокировка может происходить, если другой поток уже получил блокировку и не снял ее, либо потому, что он занят использованием списка, либо, скорее всего, он вышел из строя и не снял блокировку.

Не звони Connected() или же WriteBufferFlush() в этом коде. Вы не используете буферизацию записи, а вызываете Connected() снаружи TIdTCPServer события вызовут гоночные условия на InpuBuffer соединения, вмешиваясь в поток сервера, который управляет этим соединением, что может привести к сбоям, взаимоблокировкам, поврежденным входящим данным и т. д. Просто позвоните Write() сам по себе, и пусть он выдаст исключение, если сокет был отключен.

То, что вы показали, является небезопасным способом реализации TCP-вещания с TIdTCPServer в общем. Вместо этого вы должны реализовать потокобезопасную исходящую очередь для каждого клиента и позволить 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...
}
0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]