有时候我们调用一个第三方的会阻塞的方法,我们要想法做一个调用超时值,一般来说就是另起一个线程加join的办法,这里有另外一种思路,但也不是完全的解决办法,希望大家多多讨论。
下面是咱们的一个方法,很简单,一个执行方法,一个终止方法,一个和执行方法签名相同的公开委托。
{
public delegate int ExecuteDelegate(int a);
volatile bool _stopFlag = false;
public int Execute(int a)
{
Console.WriteLine("Execute方法执行线程:{0}",Thread.CurrentThread.GetHashCode());
int i = 0;
while (!_stopFlag)
{
Thread.Sleep(1000);
if(++i >= a)
return i;
}
return i;
}
public void AbortExecute()
{
_stopFlag = true;
Console.WriteLine("终止操作");
}
}
上面的类的Excute方法是阻塞的,如果传入的a参数是一个特别大的值,调用这个方法的代码会阻塞很长的时间,如果客户端的很多请求都要调用这个方法,而且你的系统实现了多线程,用线程池线程来处理用户的每一个请求,这就有问题了,没过多久,你的线程池就耗尽了。这时候用下面的办法可以加上一个超时逻辑。
{
MyHelper h = new MyHelper();
MyHelper.ExecuteDelegate d = h.Execute;
IAsyncResult result = (IAsyncResult)d.BeginInvoke(5,
delegate(IAsyncResult ar)
{
int temp = d.EndInvoke(ar);
Console.WriteLine("回调方法执行线程:{0}", Thread.CurrentThread.GetHashCode());
Console.WriteLine("执行结果是:{0}", temp);
}, h);
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
delegate(object state, bool timedOut)
{
if (timedOut)
{
try
{
((MyHelper)state).AbortExecute();
}
catch (Exception ex)
{
Trace.TraceError("终止操作出错:{0}",ex);
}
Console.WriteLine("超时处理线程:{0},已超时",Thread.CurrentThread.GetHashCode());
}
else
{
Console.WriteLine("超时处理线程:{0},未超时", Thread.CurrentThread.GetHashCode());
}
},h, 3000, true);
}
以上的示例代码调用的Excute方法要执行5秒,而我们设置了3秒的超时时间,到了3秒咱们就终止调用。以后如果自己要写这种可能会阻塞的方法就加一个Abort的方法来终止操作并清理资源,像HttpWebRequest类就是这么设计的,有一个Abort方法。
但是:如果这个会阻塞的方法不是你写的,是第三方提供的,而且还没有Abort方法,这时候虽然RegisterWaitForSingleObject的超时回调会执行,但是BeginInvoke执行的委托还会在线程池里继续执行,也就是还是有可能把线程池耗尽,我的建议是对于不了解的第三方方法,或者已知会阻塞的方法,不要让线程池去调用它。线程池适合处理那种快速返回的方法。
再有一个人们就说了,我自己实现一套线程池,线程池里执行一个方法超时后,我就调用Thread.Abort来终止这个线程,呵呵,想的倒挺好,.net里如果有个线程调用了非托管的代码,如果你Abort了这个线程,这个线程不会立刻抛出ThreadAbort异常,而会等待非托管代码返回托管代码才会抛出异常,那你还是没解决问题。像这种情况很多,就说常用的Socket.BeginConnect吧,虽说是异步的,可也是有可能阻塞个几十秒的,其实大多时间在DNS解析上。像这种问题,基本上没解,除非你自己去重写Windows的Socket实现去吧,貌似用c++写的Socket.connect函数也不好控制超时参数,也是用消息循环或者多个线程来实现,说是有个注册表键值,貌似也不怎么管用。
总结:很郁闷,没找到答案,也许我把问题想复杂了。
参考链接:http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!1586.entry
强烈建议dudu自动把http开头的文字加上超链接,每次我还得自己加。