TIdTCPServer上下文上的应用程序挂起->;锁名单() 我在C++ Builder XE2中开发了所谓的“Pull”应用程序,它使用2个内置的DYY组件——TeTCPPoCube和TyTcPyver。TIdTCPClient用于接收来自一个源的数据,形成字符串消息,而不是使用TIdTCPClient将此字符串消息发送到所有应用程序客户端。对于数据重传,我使用下一个函数(idEventsServerSocket是TIdTCPSever组件):
正在导致应用程序挂起。当函数向所有客户机发送数据时(比如说,每10-50毫秒调用一次),通常不会出现这种情况。客户端连接的数量从30到50不等。 我需要知道的是,有没有办法避免这种僵局?我有没有什么支票(比如TRYERCRiticalSection)? 我还想承认,Remy的解决方案没有帮助。TIdTCPServer上下文上的应用程序挂起->;锁名单() 我在C++ Builder XE2中开发了所谓的“Pull”应用程序,它使用2个内置的DYY组件——TeTCPPoCube和TyTcPyver。TIdTCPClient用于接收来自一个源的数据,形成字符串消息,而不是使用TIdTCPClient将此字符串消息发送到所有应用程序客户端。对于数据重传,我使用下一个函数(idEventsServerSocket是TIdTCPSever组件):,c++,deadlock,indy10,C++,Deadlock,Indy10,正在导致应用程序挂起。当函数向所有客户机发送数据时(比如说,每10-50毫秒调用一次),通常不会出现这种情况。客户端连接的数量从30到50不等。 我需要知道的是,有没有办法避免这种僵局?我有没有什么支票(比如TRYERCRiticalSection)? 我还想承认,Remy的解决方案没有帮助。LockList()需要在try块之外 上下文列表在内部使用关键部分,因此将此代码包装到您自己的关键部分是多余的 LockList()可以阻止的唯一方法是,如果另一个线程已经获得了锁,但没有释放它,这可能是
LockList()
需要在try
块之外
上下文
列表在内部使用关键部分,因此将此代码包装到您自己的关键部分是多余的
LockList()
可以阻止的唯一方法是,如果另一个线程已经获得了锁,但没有释放它,这可能是因为它正忙于使用该列表,也可能是因为它崩溃了,没有释放锁
不要在此代码中调用Connected()
或WriteBufferFlush()
。您没有使用写缓冲,并且从TIdTCPServer
事件外部调用Connected()
将导致连接的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...
}
#包括
类TMyContext:public TIdServerContext
{
公众:
TIDSetreadSafeStringList*队列;
bool拥有msgsinqueue;
__fastcall TMyContext(TIdTCPConnection*A连接,TIDShrain*AYarn,TIdContextThreadList*AList=NULL)
:TIdServerContext(连接、AYarn、列表)
{
队列=新的TIdThreadSafeStringList;
HasMsgsInQueue=false;
}
__快速调用TMyContext()
{
删除队列;
}
};
__fastcall TfrmMainWindow::TfrmMainWindow(TComponent*Owner)
:t表格(所有者)
{
//在激活服务器之前设置此选项
idEventsServerSocket->ContextClass=\uu classid(TMyContext);
}
void TfrmMainWindow::SendDataToAllClient(常量字符串和消息)
{
TList*ClientsList=idEventsServerSocket->context->LockList();
尝试
{
对于(int i=0;iCount;++i)
{
TMyContext*上下文=(TMyContext*)客户端列表->项目[i];
尝试
{
TStringList*Queue=Context->Queue->Lock();
尝试
{
队列->添加(消息);
Context->HasMsgsInQueue=true;
}
__最后
{
上下文->队列->解锁();
}
}
捕获(常量异常&)
{
}
}
}
__最后
{
idEventsServerSocket->context->UnlockList();
}
}
void\uu fastcall TfrmMainWindow::idEventsServerSocketExecute(TIdContext*AContext)
{
TMyContext*ctx=(TMyContext*)文本;
如果(ctx->HasMsgsInQueue)
{
TStringList*Msgs=NULL;
尝试
{
TStringList*Queue=ctx->Queue->Lock();
尝试
{
Msgs=新的TStringList;
Msgs->Assign(队列);
队列->清除();
ctx->HasMsgsInQueue=false;
}
__最后
{
ctx->队列->解锁();
}
AContext->Connection->IOHandler->Write(Msgs);
}
__最后
{
删除MSG;
}
}
if(AContext->Connection->IOHandler->InputBufferIsEmpty())
{
AContext->Connection->IOHandler->CheckForDataOnSource(100);
AContext->Connection->IOHandler->CheckForDisconnect();
if(AContext->Connection->IOHandler->InputBufferIsEmpty())
返回;
}
//根据需要处理入站数据。。。
}
非常感谢您为正确使用上下文打开我的眼睛,我没想到会有如此详细的代码。我有10.5.8.0 Indy版本,它没有定义TIdContextThreadList。我可以在TMyContext构造函数中使用System::Classes::TThreadList而不更改代码中的任何其他内容吗?还有一个问题。。。我的原始代码允许IMMIDATE重传(在我的情况下,1-2毫秒的延迟非常重要),使用您提供的代码,我将在检查输入缓冲区时获得延迟。在OnExecute事件之前,我只是读取客户机的数据,现在我甚至明白这可能是死锁的原因。CheckForDataSource中的参数是否可以从100更改为1或2而没有任何问题?是的,在早期版本中使用TThreadList
(请参阅IdContext.pas
中的TIdContext
构造函数声明)。至于延迟,是的,您可以减少使用的超时。但是,如果您的OnExecute
处理程序只需要读,不需要写,那么您就不需要使用我展示的方法。在一个线程中读取而在另一个线程中写入是安全的,无需同步对套接字的访问。我只是不建议在主UI线程中使用发送循环。被阻止的客户端将阻止整个循环,这就是我移动写操作的原因
ClientsList = idEventsServerSocket->Contexts->LockList();
#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...
}