Что определяет количество времени, которое занимает «ожидание»? - PullRequest
2 голосов
/ 14 июня 2019

У меня есть следующий код для чтения байтов из потока TCP (я удалил проверку ошибок):

/// <summary>Awaitable. Reads a certain amount of bytes from a network stream. Returns false
/// on error</summary>
async Task<bool> readBytes (NetworkStream stream, byte []buffer, int totalBytes)
{
    int bytesRead = 0;

    while (true)
    {
        var br = await stream.ReadAsync (buffer, bytesRead, totalBytes - bytesRead);
        if (br == 0) return false;      // closed stream

        bytesRead += br;
        if (bytesRead >= totalBytes) return true;
    }
}

Затем я использую его внешне, чтобы получить 4 байта с длиной поступающих данных,и массив данных (псевдокод, я удалил проверку ошибок и распределение, просто чтобы дать представление):

success = await readBytes(stream, header, 4);
success = await readBytes(stream, data, dataLength);

presentDataToApp(data, dataLength);

Клиент / сервер должен взаимодействовать почти в реальном времени (это игра, где клиентское приложениерегулирует несколько параметров, например, свет).Например, клиент имеет ползунок WinForms и передает все изменения в серверное приложение.При перемещении этого ползунка может быть много обновлений в одну секунду (из моих журналов он отправляет данные примерно 100 раз в секунду).

Этот код работает хорошо и в режиме реального времени, пока сервер (который запускает код, который я вставил) запускается через Visual Studio.Однако, когда я запускаю серверное приложение без Visual Studio (или использую Ctrl + F5 ), связь начинает сильно отставать.

Чтобы отменить проблему асинхронности / ожидания,Я клонировал класс comms и запустил его через Thread, удалив все функции async / await.Теперь код выполняется в реальном времени как в Visual Studio, так и в автономном режиме (поэтому это проблема, связанная с async / await).

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

1 Ответ

2 голосов
/ 14 июня 2019

Клиент / сервер должен взаимодействовать практически в реальном времени

Я всегда рекомендую SignalR в любом случае, когда вы контролируете и клиента, и сервер.Программирование на SignalR намного проще, чем на сырых сокетах.

Этот код хорошо работает и в режиме реального времени, если сервер работает через Visual Studio.Однако, когда я запускаю приложение сервера без Visual Studio (или с помощью Ctrl + F5), связь начинает сильно отставать.

Это очень странно.VS заставляет ваш код работать быстрее? У меня нет объяснения этому.

Что определяет количество времени, которое ожидалось после ожидания, когда данные не были готовы??

Здесь происходит несколько вещей, каждая из которых может оказать влияние.

Во-первых, await захватывает контекст и возобновляет работу в этом контексте.Это особенно проблема, если это контекст пользовательского интерфейса. При 100 обновлениях в секунду вы достигнете предела практического .Если ваши await s возобновляются в потоке пользовательского интерфейса, вы можете избежать такого рода "снижения производительности на тысячу сокращений бумаги", используя ConfigureAwait(false).Если это действительно await, что вызывает замедление, найдите время, чтобы просмотреть Zen of Async video .

Во-вторых, существует большой отток памяти.readBytes требует отдельного массива для каждого пакета как минимум (при условии, что вы повторно используете массив заголовков), а также существует несколько экземпляров Task. Если профилирование предполагает, что проблема заключается в оттоке памяти, тогда рассмотрите возможность использования новых API-интерфейсов на основе сокетов Memory<T>, которые позволяют избежать байтовых массивов, а также новых API-интерфейсов на основе ValueTask<T>, которые позволяют избежатьраспределение Task, когда данные уже получены .Например, .NET Core 2.1 Stream API ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken); Если профилирование предполагает, что проблема заключается в оттоке памяти, но вы еще не используете .NET Core 2.1, тогда вы можете использовать SocketAsyncEventArgs API .

...