Dot-Net
帶有取消令牌的 NetworkStream.ReadAsync 永遠不會取消
這裡證明。
知道這段程式碼有什麼問題嗎?
[TestMethod] public void TestTest() { var tcp = new TcpClient() { ReceiveTimeout = 5000, SendTimeout = 20000 }; tcp.Connect(IPAddress.Parse("176.31.100.115"), 25); bool ok = Read(tcp.GetStream()).Wait(30000); Assert.IsTrue(ok); } async Task Read(NetworkStream stream) { using (var cancellationTokenSource = new CancellationTokenSource(5000)) { int receivedCount; try { var buffer = new byte[1000]; receivedCount = await stream.ReadAsync(buffer, 0, 1000, cancellationTokenSource.Token); } catch (TimeoutException e) { receivedCount = -1; } } }
我終於找到了解決方法。使用 Task.WaitAny 將非同步呼叫與延遲任務 (Task.Delay) 相結合。當延遲在 io 任務之前過去時,關閉流。這將強制任務停止。您應該正確處理 io 任務的非同步異常。並且您應該為延遲任務和 io 任務添加一個延續任務。
它也適用於 tcp 連接。在另一個執行緒中關閉連接(您可以認為它是延遲任務執行緒)會強制使用/等待此連接停止的所有非同步任務。
- 編輯 -
@vtortola 建議的另一個更清潔的解決方案:使用取消令牌註冊對流的呼叫。關閉:
async ValueTask Read(NetworkStream stream, TimeSpan timeout = default) { if(timeout == default(TimeSpan)) timeout = TimeSpan.FromSeconds(5); using var cts = new CancellationTokenSource(timeout); //C# 8 syntax using(cts.Token.Register(() => stream.Close())) { int receivedCount; try { var buffer = new byte[30000]; receivedCount = await stream.ReadAsync(buffer, 0, 30000, tcs.Token).ConfigureAwait(false); } catch (TimeoutException) { receivedCount = -1; } } }