什么决定“等待”所花费的时间?

时间:2019-06-14 10:37:18

标签: c# asynchronous async-await

我有以下代码从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类并通过线程运行它,删除了所有异步/等待函数。该代码现在可以在Visual Studio和独立版本中实时运行(因此,这是与异步/等待相关的问题)。

在这种情况下,为什么异步/等待通信滞后? (在一秒钟内肯定会有很多等待)。如何确定没有准备好数据的等待所花费的等待时间? (我以为这是计时器分辨率,但是无论我如何执行服务器应用程序,它在系统上似乎都是1毫秒)。

1 个答案:

答案 0 :(得分:2)

  

客户端/服务器必须几乎实时交互

在您同时控制客户端和服务器的任何情况下,我始终建议使用SignalR。针对SignalR进行编程比使用原始套接字要容易得多。

  

只要服务器通过Visual Studio运行,此代码就可以很好地实时运行。但是,当我在不使用Visual Studio(或使用Ctrl + F5)的情况下运行服务器应用程序时,通信开始严重滞后。

这很奇怪。 VS使您的代码更快地运行?我对此没有任何解释。

  

如何确定没有准备好数据的等待后等待的时间?

这里发生的几件事可能都会产生影响。

第一个是await捕获上下文并在该上下文上恢复。如果它是UI上下文,这尤其是个问题。 At 100 updates per second, you're reaching the limit of what's practical。如果您的await正在UI线程上恢复,则可以通过使用ConfigureAwait(false)来避免这种“一千次剪纸导致的性能下降”。如果确实是await导致了速度下降,请花些时间查看Zen of Async video

第二个原因是内存流失很多。 readBytes至少每个数据包都需要一个单独的数组(假设您重复使用标头数组),并且周围还有Task个实例。 如果分析表明问题是内存流失,则consider using the new Memory<T>-based socket APIs that avoid the byte arrays, as well as the new ValueTask<T>-based APIs which avoid the Task allocations when the data is already arrived。例如,.NET Core 2.1 Stream API ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken);如果分析表明问题是内存流失,但您尚未使用.NET Core 2.1,则可以使用SocketAsyncEventArgs APIs。 / p>