我正在使用.NET C#开发TCP服务器。它使用异步I / O(I / O完成)来同时处理大量客户端。现在我在列表中有所有TCP连接,我不断寻找任何特定连接的状态机的变化。一旦I / O完成设置了某些标志,就会更新给定连接的状态机。
我确信有更好的方法可以做到这一点 - 当前的实现非常耗费处理器,因为我没有阻止等待更新,而是在没有限制的情况下进行轮询。我真的不在乎我的服务器是否在浪费周期,但我猜它的设计很糟糕。我试图找到一种方法来处理特定的连接,只有当I / O完成信号有待处理的东西,并等待(即睡眠)时。任何人都可以建议一个好方法吗?
我认为某些线程同步事情可能会在循环的主线程等待任何I / O完成释放它的地方起作用。但是,有时使用调用线程执行I / O完成(当数据立即可用时等),因此这会导致此解决方案出现问题。
你能提出的任何建议都会非常感激!
这是主线程执行的(简化)循环(rgClient是客户端列表):
//Do communications on each client we currently have connected.
//This loops runs backwards so we can delete elements on the fly
//without have to iterate through more than once.
lock (rgClient)
{
for (i = rgClient.Count - 1; i >= 0; i--)
{
if (!rgClient[i].DoComm())
{
rgClient[i].DoClose();
rgClient.RemoveAt(i);
}
}
}
DoComm()在连接的状态机上执行更新,包括执行当前状态的活动,然后在必要时转换到新状态。这是发送简单“ack”数据包的状态类:
class StateAck : State
{
public StateAck(TextBox txtOutputExt, Form fmOwner)
: base(txtOutputExt, fmOwner)
{
fWriting = false;
}
public override bool DoExecute(out Type tpNextState)
{
PktAck pkt;
if (!base.DoExecute(out tpNextState))
{
return false;
}
//Start a write if we haven't yet
if (!fWriting)
{
pkt = new PktAck();
fWriting = true;
return FPutPkt(pkt.rgbSerialize());
}
//Is the read finished / has an error occurred?
if (fDataErrorWrite)
{
return false;
}
//Process the data
if (fDataWritten)
{
tpNextState = typeof(StateIdle);
}
return true;
}
private bool fWriting;
}
每次从主线程调用DoComm()时,执行都会通过DoExecute()。 99%的时间,实际上没有发生任何事情。当写入(通过调用FPutPkt()启动)完成时,标志将发出信号,然后将下一个状态设置为“空闲”。我想要做的是,主线程只检查已完成网络活动并且需要更新的客户端,以避免通过DoExecute()进行常量和冗余传递。
答案 0 :(得分:0)
我找到了一个似乎运作良好的解决方案。使用EventWaitHandler(System.Threading)自动重置到每次循环底部的WaitOne()。然后任何回调或辅助线程都可以通过调用EWH.Set()来通知EventWaitHandler,这将允许循环进行另一次传递。在不对程序流进行重大修改的情况下消除轮询循环的CPU使用率的好方法。希望它可以帮到某人。