蛙蛙推荐:设计一个可超时的阻塞方法

有时候我们调用一个第三方的会阻塞的方法,我们要想法做一个调用超时值,一般来说就是另起一个线程加join的办法,这里有另外一种思路,但也不是完全的解决办法,希望大家多多讨论。
下面是咱们的一个方法,很简单,一个执行方法,一个终止方法,一个和执行方法签名相同的公开委托。

class MyHelper
{
    
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(++>= a)
                
return i;
        }

        
return i;
    }

    
public void AbortExecute()
    
{
        _stopFlag 
= true;
        Console.WriteLine(
"终止操作");
    }

}



上面的类的Excute方法是阻塞的,如果传入的a参数是一个特别大的值,调用这个方法的代码会阻塞很长的时间,如果客户端的很多请求都要调用这个方法,而且你的系统实现了多线程,用线程池线程来处理用户的每一个请求,这就有问题了,没过多久,你的线程池就耗尽了。这时候用下面的办法可以加上一个超时逻辑。

private static void InvokeExcute()
{
    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, 3000true);
}

以上的示例代码调用的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开头的文字加上超链接,每次我还得自己加。

原文地址:https://www.cnblogs.com/onlytiancai/p/1166512.html