SocketAsyncEventArgs и безопасность потоков в .Net - PullRequest
1 голос
/ 18 мая 2011

Я использовал примеры из MSDN и (в основном) CodeProject для написания сокет-сервера.Я пытаюсь осмыслить потокобезопасность кода.Все события сокета инициируют метод IO_Completed, который проверяет SAEA на предмет последнего типа операции (отправка или получение):

void IO_Completed(object sender, SocketAsyncEventArgs e)
{
    // determine which type of operation just completed and call the associated handler
    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Receive:
            ProcessReceive(e);
            break;
        case SocketAsyncOperation.Send:
            ProcessSend(e);
            break;
        default:
            throw new ArgumentException("The last operation completed on the socket was not a receive or send");
    }       
}

Принимая во внимание входящие вызовы, нужно ли ProcessReceive () быть полностью поточно-ориентированным, поскольку этовызывается много раз за короткий промежуток времени, если клиентов много, или он как-то блокируется, чтобы полностью завершиться до следующего вызова этого события?Я делаю больше, чем просто возвращаю полученное сообщение обратно клиенту (что и делают примеры).

Даже в примерах ProcessReceive () является довольно длинным методом (см. Ниже) и, безусловно, долженбыть под угрозой коррупции из второго потока.К тому времени, когда я добавляю код, мне нужно сделать что-то осмысленное (вызвать службу WCF), вероятность повторного запуска того же кода должна быть очень высокой.

Что мне нужно сделать, чтобы сделать ProcessReceive ()(и другие связанные методы), как правило, потокобезопасны без ущерба для производительности, получаемой от использования SocketAsyncEventArgs?

Пример метода ProcessReceive () ниже:

private void ProcessReceive(SocketAsyncEventArgs receiveSendEventArgs)
{
    DataHoldingUserToken receiveSendToken =
                 (DataHoldingUserToken)receiveSendEventArgs.UserToken;

    if (receiveSendEventArgs.SocketError != SocketError.Success)
    {
        receiveSendToken.Reset();
        CloseClientSocket(receiveSendEventArgs);
        return;
    }

    if (receiveSendEventArgs.BytesTransferred == 0)
    {
        receiveSendToken.Reset();
        CloseClientSocket(receiveSendEventArgs);
        return;
    }

    Int32 remainingBytesToProcess = receiveSendEventArgs.BytesTransferred;

    if (receiveSendToken.receivedPrefixBytesDoneCount <
                       this.socketListenerSettings.ReceivePrefixLength)
    {
        remainingBytesToProcess = prefixHandler.HandlePrefix(receiveSendEventArgs,
                  receiveSendToken, remainingBytesToProcess);

        if (remainingBytesToProcess == 0)
        {
            StartReceive(receiveSendEventArgs);
            return;
        }
    }

    bool incomingTcpMessageIsReady = messageHandler
              .HandleMessage(receiveSendEventArgs,
              receiveSendToken, remainingBytesToProcess);

    if (incomingTcpMessageIsReady == true)
    {
        receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder);
        receiveSendToken.CreateNewDataHolder();
        receiveSendToken.Reset();
        receiveSendToken.theMediator.PrepareOutgoingData();
        StartSend(receiveSendToken.theMediator.GiveBack());
    }
    else
    {
        receiveSendToken.receiveMessageOffset = receiveSendToken.bufferOffsetReceive;
        receiveSendToken.recPrefixBytesDoneThisOp = 0;
        StartReceive(receiveSendEventArgs);
    }
}

Ответы [ 3 ]

1 голос
/ 18 мая 2011

Просто синхронизируйте то, что нужно синхронизировать.* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

* * * * * * * * * * * * * * * * [0006] * * * * * * * * * * (и другие переменные, такие как * prefixHandler) не потокобезопасны, поэтому они должны бытьзащищенный.Насколько я могу сказать, простой lock должен подойти.

Ментальная модель такова: IO_Completed может быть вызван в любое время с разными аргументами;каждый из них работает в потоке ThreadPool.

0 голосов
/ 18 мая 2011

Я бы порекомендовал использовать модель асинхронного программирования, в основном клиенты будут вызывать BeginProcessReceive и передавать обратный вызов, а в обратном вызове выполнять EndProcessReceive. Вы можете использовать либо задачи, либо, если до версии 4.0 вызывать ThreadPool.QueueUserWorkItem. Я предполагаю, что здесь, но похоже, что StartReceived или StartSend являются блокирующими методами, которые могут выполняться в своем собственном потоке (thread-pool). Вызов службы WCF, как вы упомянули, поддается этой модели.

Эта модель позволит вам обрабатывать большое количество клиентов, помимо множества других преимуществ ...

0 голосов
/ 18 мая 2011

Я недавно реализовал нечто подобное.Он обрабатывал сообщения через TCP-соединения.Я создал один поток, который отвечал за прием входящих соединений.Затем этот поток создаст новый поток для обработки каждого соединения.Эти потоки блокировались во время ожидания ввода-вывода из сети, поэтому они не использовали ресурсы ЦП.Если ваши соединения ничего не делят, это не требует безопасности потоков.

...