提供有关三种不同方法的更多背景信息。我的服务监控其他 Web 应用程序的可用性。因此,它需要与各种网站建立大量连接。其中一些崩溃/返回错误/变得无响应。
轴 Y - 挂起测试(会话)的数量。由于部署/重新启动而下降到 0。
I.(1 月 25 日)改造服务后,初始实现使用带有取消令牌的 ReadAsync。这导致大量测试挂起(对这些网站运行请求表明服务器有时确实没有返回内容)。
二、(2 月 17 日)部署了使用 Task.Delay 保护取消的更改。这完全解决了这个问题。
private async Task<int> StreamReadWithCancellationTokenAsync(Stream stream, byte[] buffer, int count, Task cancellationDelayTask)
{
if (cancellationDelayTask.IsCanceled)
{
throw new TaskCanceledException();
}
// Stream.ReadAsync doesn't honor cancellation token. It only checks it at the beginning. The actual
// operation is not guarded. As a result if remote server never responds and connection never closed
// it will lead to this operation hanging forever.
Task<int> readBytesTask = stream.ReadAsync(
buffer,
0,
count);
await Task.WhenAny(readBytesTask, cancellationDelayTask).ConfigureAwait(false);
// Check whether cancellation task is cancelled (or completed).
if (cancellationDelayTask.IsCanceled || cancellationDelayTask.IsCompleted)
{
throw new TaskCanceledException();
}
// Means that main task completed. We use Result directly.
// If the main task failed the following line will throw an exception and
// we'll catch it above.
int readBytes = readBytesTask.Result;
return readBytes;
}
III(3 月 3 日)在此 StackOverflow 之后实现了基于超时关闭流:
using (timeoutToken.Register(() => stream.Close()))
{
// Stream.ReadAsync doesn't honor cancellation token. It only checks it at the beginning. The actual
// operation is not guarded. As a result if a remote server never responds and connection never closed
// it will lead to this operation hanging forever.
// ReSharper disable once MethodSupportsCancellation
readBytes = await targetStream.ReadAsync(
buffer,
0,
Math.Min(responseBodyLimitInBytes - totalReadBytes, buffer.Length)).ConfigureAwait(false);
}
这个实现带来了后顾之忧(与最初的方法不同):
恢复为 Task.Delay 解决方案。