蛙蛙池塘  
人生价值的最好体现就是做好本职工作...
公告
  • 残荷听雨,梨花飞雪,落英缤纷时节。晓来谁染枫林醉?点点都是离人泪
    活着,就是快乐!自信,就是美丽! 有人爱,就是幸福。
    春天来了
    但愿野百合也有春天

    第三季度的计划



    木了
    晚上一个人看会儿《读者乡土人文版》,听会儿广播挺不错的,想起了三年前在石家庄没电脑的日子,时光飞逝呀,现在笔记本都用上了,以前从没想过,确实得知足常乐。
日历
<2008年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
统计
  • 随笔 - 249
  • 文章 - 2
  • 评论 - 2358
  • 引用 - 75

导航

与我联系

搜索

 

常用链接

留言簿

我参加的小组

我参与的团队

我的标签

随笔分类

随笔档案

相册

朋友

积分与排名

  • 积分 - 542646
  • 排名 - 49

最新评论

阅读排行榜

评论排行榜

60天内阅读排行

 

有时候我们调用一个第三方的会阻塞的方法,我们要想法做一个调用超时值,一般来说就是另起一个线程加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开头的文字加上超链接,每次我还得自己加。

Tag标签: 超时
posted on 2008-04-22 23:12 蛙蛙池塘 阅读(1850) 评论(17)  编辑 收藏 网摘 所属分类: 综合区
评论:
  • #1楼  平静中的疯狂       Posted @ 2008-04-22 23:37
    夜深了
    http://www.cnblogs.com/Emoticons/tusiji/20333056.gif" alt="" />漂过
      回复  引用  查看    

  • #2楼  Yannic Yang Posted @ 2008-04-22 23:50
    最近也接触了些异步调用的问题,正是socket,这方面我没什么经验,有时候确实会有不少奇怪的问题……比如那个连接断开却不抛异常问题很头痛,都等待解决中   回复  引用    

  • #3楼  Jeffrey Zhao       Posted @ 2008-04-23 00:44
    DNS解析?这个也可以是异步的。   回复  引用  查看    

  • #4楼  Jeffrey Zhao       Posted @ 2008-04-23 00:46
    关于节省线程池,还是必须使用几个特定的借助IO Port的异步操作。当然你这个属于避免执行太长时间,从某个角度上说也算一种节省吧。   回复  引用  查看    

  • #5楼  李战       Posted @ 2008-04-23 08:19
    http://www.cnblogs.com/Emoticons/yoyocici/223854210.gif" alt="" />收下,慢慢看,谢谢   回复  引用  查看    

  • #6楼  good man       Posted @ 2008-04-23 08:57
    好文章哟,学习   回复  引用  查看    

  • #7楼  生鱼片       Posted @ 2008-04-23 09:23
    学习   回复  引用  查看    

  • #8楼  overred       Posted @ 2008-04-23 09:40
    不错。。。
    我一般也是在第三方的方法体外再套个线程壳
    然后控制那个壳
      回复  引用  查看    

  • #9楼  PerfectDesign       Posted @ 2008-04-23 11:18
    没有太看代码,不过好像这种解决方案最好使用manualresetevent
    autoresetevent
    用于一个线程做完一部分事情之后通知另外一个线程开始工作,然后自己挂起,然后又接受通知自己运行。
      回复  引用  查看    

  • #10楼  lexus       Posted @ 2008-04-23 11:46
    前几天我也在想给线程池中执行的方法限定时间,我当时的想法是使用一个线程去检测方法执行的时间,到点去中断执行方法的线程,不过还没有想明白具体如何实现   回复  引用  查看    

  • #11楼  Jinx[未注册用户] Posted @ 2008-04-23 12:14
    同意9楼的做法,楼主这样的做法不但效率不高,而且使用不灵活,结果不能很实时的返回!   回复  引用    

  • #12楼[楼主]  蛙蛙池塘       Posted @ 2008-04-23 12:57
    @Jinx
    这个也是用了信号等待,result.AsyncWaitHandle也是一个WaitHandler,在线程池里注册一个信号,还省了一个你等待信号的线程呢,不是更好吗?你可以看下IAsyncResult.AsyncWaitHandle和ThreadPool.RegisterWaitForSingleObject的说明。
    @Jeffrey Zhao
    其实目的不是为了节省什么,是为了防止线程耗尽或者线程池耗尽。
      回复  引用  查看    

  • #13楼  侯垒       Posted @ 2008-04-23 13:59
    正用到了这个.谢谢.   回复  引用  查看    

  • #14楼  wuliangbo       Posted @ 2008-04-23 14:28
    @Jinx
    这个也是用了信号等待,result.AsyncWaitHandle也是一个WaitHandler,在线程池里注册一个信号,还省了一个你等待信号的线程呢,不是更好吗?你可以看下IAsyncResult.AsyncWaitHandle和 ThreadPool.RegisterWaitForSingleObject的说明。
    @Jeffrey Zhao
    其实目的不是为了节省什么,是为了防止线程耗尽或者线程池耗尽。



    ss
      回复  引用  查看    

  • #15楼  镜涛       Posted @ 2008-04-23 14:42
    --引用--------------------------------------------------
    李战: <img src="http://www.cnblogs.com/Emoticons/yoyocici/223854210.gif"" target="_new">http://www.cnblogs.com/Emoticons/yoyocici/223854210.gif" alt="" />收下,慢慢看,谢谢

    --------------------------------------------------------
      回复  引用  查看    

  • #16楼  varptr       Posted @ 2008-04-24 16:50
    使用BeginInvoke也会产生额外的线程。
    有时要从整个程序的运行来考虑问题,局部稍显复杂了。
      回复  引用  查看    

  • #17楼[楼主]  蛙蛙池塘       Posted @ 2008-04-25 09:06
    @varptr
    但那是线程池线程,是可重用的
      回复  引用  查看    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1166512




相关文章:

相关链接:
 
Copyright © 蛙蛙池塘 Powered by: 博客园 模板提供:沪江博客