- socket 底层Connect()方法,如果存在端口错误,或是监控端口server 未开启则,connect()会在10~30返回结果。
这个时间太长了,非常影响用户体验。 - StackOverflow的相关链接:
1
代码测试
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Sockets;
using System.Net;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Method3();
Console.ReadKey();
}
static bool Method3()
{
var client = new TcpClient();
if (!client.ConnectAsync("192.168.1.131", 50000).Wait(1000))
{
//连接失败
Console.WriteLine("连接失败");
return false;
}
Console.WriteLine("连接成功");
return true;
}
}
}
- 使用委托beginInvoke()的轮询模式。轮询模式
在调用beginInvoke时将新建一个线程异步执行,使用轮询模式,判断方式是否已执行完成,并判断其是否超时。
缺点: 当超时时,无法abort在运行的线程。
代码测试
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
delegate void Mydel();
class Program
{
static void Main(string[] args)
{
///使用轮询模式,监控异步方法是否超时
Mydel del = new Mydel(Method);
Stopwatch watch = new Stopwatch();
watch.Start();
IAsyncResult iar = del.BeginInvoke(null, null);//开始异步调用
Console.WriteLine("After BeginInvoke");
while (!iar.IsCompleted)
{
Console.WriteLine("Not Done");
//判断是否超时,若超时,则重启读写器
long time = watch.ElapsedMilliseconds;
if (time > 1500)
{
Console.WriteLine("异步方法超时");
//超时
break;
}
System.Threading.Thread.Sleep(100);
Console.WriteLine("下一轮查询");
}
Console.ReadKey();
}
/// <summary>
/// 需要异步执行的方法
/// </summary>
static void Method()
{
Console.WriteLine("进入方法内部");
///耗时操作
System.Threading.Thread.Sleep(4000);
Console.WriteLine("方法结束");
}
}
}
从运行结果看到, 并不能取消异步正在运行的线程。被异步调用的方法,在判断已超时后,仍在运行。
有没有可以取消已在运行的线程呢?使用CancellationTokenSource看一下取消线程运行的机制
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Method();
Console.ReadKey();
}
static void Method()
{
var CancellationTokenSource = new CancellationTokenSource(3000);
Task.Factory.StartNew(() =>
{
while (!CancellationTokenSource.IsCancellationRequested)
{
Console.WriteLine(DateTime.Now);
Thread.Sleep(1000);
}
}, CancellationTokenSource.Token);
//Console.WriteLine("Press any to cancel");
//Console.ReadLine();
//CancellationTokenSource.Cancel();
Console.WriteLine("done");
Console.ReadLine();
}
}
}
但是实际的业务需求中,并不能用CancellationTokenSource来取消正在运行的线程。
如果用来封装第三方的某个方法以实现可以自定义超时时间,使用CancellationTokenSource是不行。
- 使用线程: 2
代码测试
using System;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
try
{
TcpClient connection = new TcpClientWithTimeout("192.168.1.131", 50000, 1000).Connect();
if(connection.Client != null)
{
Console.WriteLine("连接成功");
}
}
catch (Exception e)
{
Console.WriteLine("连接失败");
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
public class TcpClientWithTimeout
{
protected string _hostname;
protected int _port;
protected int _timeout_milliseconds;
protected TcpClient connection;
protected bool connected;
protected Exception exception;
public TcpClientWithTimeout(string hostname, int port, int timeout_milliseconds)
{
_hostname = hostname;
_port = port;
_timeout_milliseconds = timeout_milliseconds;
}
public TcpClient Connect()
{
// kick off the thread that tries to connect
connected = false;
exception = null;
Thread thread = new Thread(new ThreadStart(BeginConnect));
thread.IsBackground = true; // So that a failed connection attempt
// wont prevent the process from terminating while it does the long timeout
thread.Start();
// wait for either the timeout or the thread to finish
thread.Join(_timeout_milliseconds);
if (connected == true)
{
// it succeeded, so return the connection
thread.Abort();
return connection;
}
if (exception != null)
{
// it crashed, so return the exception to the caller
thread.Abort();
throw exception;
}
else
{
// if it gets here, it timed out, so abort the thread and throw an exception
thread.Abort();
string message = string.Format("TcpClient connection to {0}:{1} timed out",
_hostname, _port);
throw new TimeoutException(message);
}
}
protected void BeginConnect()
{
try
{
connection = new TcpClient(_hostname, _port);
// record that it succeeded, for the main thread to return to the caller
connected = true;
}
catch (Exception ex)
{
// record the exception for the main thread to re-throw back to the calling code
exception = ex;
}
}
}
}
这个方式虽然不是太简介,但是实现了在方法执行超时时,取消在已开启的线程。可以有效管理线程资源。