那我该怎么办?
在这种特殊情况下,我宁愿优雅地使用UdpClient.Client.ReceiveTimeout
和TcpClient.ReceiveTimeout
超时 UDP 或 TCP 接收操作。我希望超时错误来自套接字,而不是来自任何外部来源。
如果除此之外我还需要观察其他一些取消事件,例如 UI 按钮单击,我会使用WithCancellation
Stephen Toub 的“如何取消不可取消的异步操作?” , 像这样:
using (var client = new UdpClient())
{
UdpClient.Client.ReceiveTimeout = 2000;
var result = await client.ReceiveAsync().WithCancellation(userToken);
// ...
}
为了解决评论,如果ReceiveTimeout
对 没有影响ReceiveAsync
,我仍然会使用WithCancellation
:
using (var client = new UdpClient())
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(userToken))
{
UdpClient.Client.ReceiveTimeout = 2000;
cts.CancelAfter(2000);
var result = await client.ReceiveAsync().WithCancellation(cts.Token);
// ...
}
IMO,这更清楚地表明了我作为开发人员的意图,并且对第 3 方来说更具可读性。另外,我不需要捕捉ObjectDisposedException
异常。我仍然需要观察OperationCanceledException
调用此方法的客户端代码中的某个位置,但无论如何我都会这样做。OperationCanceledException
通常从其他例外中脱颖而出,我可以选择检查OperationCanceledException.CancellationToken
以观察取消的原因。
除此之外,与@I3arnon 的回答没有太大区别。我只是觉得我不需要另一种模式,因为我已经可以使用WithCancellation
了。
要进一步解决评论:
- 我只会
OperationCanceledException
在客户端代码中捕获,即:
async void Button_Click(sender o, EventArgs args) { try { await DoSocketStuffAsync(_userCancellationToken.Token); } catch (Exception ex) { while (ex is AggregateException) ex = ex.InnerException; if (ex is OperationCanceledException) 返回;// 如果取消则忽略 // 否则报告 MessageBox.Show(ex.Message); } }
- 是的,我会在
WithCancellation
每次ReadAsync
通话时使用,我喜欢这个事实,原因如下。首先,我可以创建一个扩展ReceiveAsyncWithToken
:
public static class UdpClientExt
{
public static Task<UdpReceiveResult> ReceiveAsyncWithToken(
this UdpClient client, CancellationToken token)
{
return client.ReceiveAsync().WithCancellation(token);
}
}
其次,从现在起的 3 年内,我可能会审查 .NET 6.0 的代码。届时,微软可能会有一个新的 API UdpClient.ReceiveAsyncWithTimeout
,. 就我而言,我将简单地将ReceiveAsyncWithToken(token)
orReceiveAsync().WithCancellation(token)
替换为ReceiveAsyncWithTimeout(timeout, userToken)
. 处理起来不会那么明显CreateTimeoutScope
。