到目前为止,我找到的所有解决方案都基于WaitOne: How to configure socket connect timeout 或spawning a worker thread
对我来说,用WaitOne
阻塞线程会破坏异步方法的目的。产生另一个线程并不是更好(因为异步模型努力使用尽可能少的线程)。
是否有任何其他解决方案仍然允许我在不阻塞当前线程或产生另一个线程的情况下中止连接尝试?
我正在开发一个外部代码使用的库,它不知道我的库里面发生了什么(代码只调用我的ConnectAsync方法,其余的包括TcpClient.ConnectAsync
等等)。外部代码可以是任何东西(Web应用程序,桌面应用程序,Windows服务,等等)。理想情况下,解决方案不应要求外部代码执行任何操作以中止操作,而不是设置我的类的.Timeout属性。但是,如果它是实现自定义连接超时时唯一避免块或工作线程的方法,我将不胜感激看看我有哪些选项(就async / await模型而言)。
答案 0 :(得分:5)
TcpClient.SendAsync
未收到CancellationToken
因此无法取消真正(How do I cancel non-cancelable async operations?)。您可以使用WithTimeout
扩展程序方法:
public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
return Task.WhenAny(task, timeoutTask).Unwrap();
}
但这并不取消原始操作,只允许您的代码表现得像一样。除非明确处理,否则被遗弃的行动将永远存在。
要实际取消基础操作,您应确保在Dispose
上调用TcpClient
(最好通过using
范围)。这将使被遗弃的任务抛出ObjectDisposedException
(或其他人),所以要注意这一点。
您可以查看我的answer here关于使用TimeoutScope
:
try
{
var client = new TcpClient();
using (client.CreateTimeoutScope(TimeSpan.FromSeconds(2)))
{
var result = await client.ConnectAsync();
// Handle result
}
}
catch (ObjectDisposedException)
{
return null;
}
答案 1 :(得分:1)
如果您为超时创建了第二个任务(Task.Delay
很好),那么您可以在任务完成或超时完成后立即使用Task.WhenAny
。
var timeout = Task.Delay(whatever);
var mightTimeout = Task.WhenAny(new {} { timeout, originalTask });
// let mightTimeout complete by whatever method (eg. async)
if (mightTimeout == timeout) {
// Timeout!!
// Put abort code in here.
}
答案 2 :(得分:1)
我找到了一个使用
的解决方案Await Task.WhenAny
Task.WhenAny将在任何包含的任务完成后完成。 把它放在异步函数
中这是一个适合我的例子:
Public Async Function TCPConnectionAsync(HostIpAddress, Port) As Task(Of String)
Dim client As New TcpClient
Await Task.WhenAny(client.ConnectAsync(HostIpAddress, Porta), Task.Delay(3000))
'this above will not block because function is async,
'whenever the connection is successful or Task.Delay expires the task will end
' proceeding next, where you can check the connection
If client.Connected = False Then 'this will be evaluated after 3000ms
client.Close()
return "not connected"
else
'do here whatever you need with the client connection
client.Close()
return "all done"
End If
End Sub