Я разрабатываю TCP-сервер в .NET C #. Он использует асинхронный ввод / вывод (завершение ввода / вывода) для одновременной обработки большого количества клиентов. Прямо сейчас у меня есть все TCP-соединения в списке, который я постоянно прохожу в поисках изменений в автомате состояний любого конкретного соединения. Конечный автомат для данного соединения обновляется, когда завершение ввода-вывода устанавливает определенные флаги.
Я уверен, что есть лучший способ сделать это - текущая реализация очень интенсивно использует процессор, так как я не блокирую ожидание обновления, а скорее опрашиваю без дросселирования. Мне все равно, если мой сервер тратит впустую циклы, но я предполагаю, что это плохой дизайн. Я пытаюсь найти способ обработать определенное соединение только тогда, когда есть сигналы завершения ввода-вывода, и есть что-то, что нужно обработать, и ждать (то есть спать), когда нет. Кто-нибудь может предложить хороший способ сделать это?
Я думал, что некоторые вещи синхронизации потоков могут работать там, где основной поток, который находится в цикле, ожидает любого завершения ввода-вывода, чтобы освободить его. Однако завершение ввода-вывода иногда выполняется с использованием вызывающего потока (когда данные сразу становятся доступны и т. Д.), Поэтому это может вызвать проблемы с этим решением.
Все, что вы можете предложить, будет высоко ценится!
Вот (упрощенный) цикл, который выполняется основным потоком (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;
}
Выполнение проходит через DoExecute () каждый раз, когда DoComm () вызывается из основного потока. 99% времени ничего не происходит на самом деле. Когда запись (которая инициируется вызовом FPutPkt ()) завершается, флаг сигнализирует об этом, а затем следующее состояние устанавливается в состояние «ожидания». Я хочу, чтобы основной поток проверял только клиентов, которые завершили свою сетевую активность и что-то требует обновления, чтобы избежать постоянных и избыточных проходов через DoExecute ().