C # .NET Server опрашивает несколько соединений - есть ли лучший способ? - PullRequest
1 голос
/ 21 ноября 2010

Я разрабатываю 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 ().

1 Ответ

0 голосов
/ 21 января 2011

Я нашел решение, которое, кажется, работает довольно хорошо.Используйте EventWaitHandler (System.Threading) с автоматическим сбросом в WaitOne () внизу каждого прохода цикла.Тогда любой обратный вызов или вторичный поток может сигнализировать EventWaitHandler, вызывая EWH.Set (), что позволит циклу сделать еще один проход.Отличный способ исключить использование ЦП цикла опроса без значительных изменений в потоке программы.Надеюсь, это кому-нибудь поможет.

...