有些方法在使用时可以会花费较多的时间,严重影响用户体验。现在给方法添加超时时间,时间一过就主动放弃。
public void CallWithTimeout(Action action, int timeoutMilliseconds) { Thread thread = null; Action wrappedAction = () => { thread = Thread.CurrentThread; action(); }; IAsyncResult result = wrappedAction.BeginInvoke(null, null); if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) //在指定时间内是否收到信号 { wrappedAction.EndInvoke(result); } else { thread.Abort(); } } public void TestProcdure() { Thread.Sleep(5000); //这个方法要5秒的执行时间 }
测试代码:
CallWithTimeout(TestProcdure, 4000); //执行时间为4秒,方法中止 CallWithTimeout(TestProcdure, 6000); //执行时间为6秒,方法正常执行
上面的方法有一个缺点,我们不知道执行成功或是中止,因为方法都没有返回值。可以针对上面的方法做一些小的调整
private string CallWithTimeout(Func<string> func, int timeoutMilliseconds) //把Action改成Func类型 { Thread thread = null; Func<string> wrappedFunc = () => { thread = Thread.CurrentThread; return func(); }; IAsyncResult result = wrappedFunc.BeginInvoke(null, null); if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) { return wrappedFunc.EndInvoke(result); //方法成功执行返回值 } else { thread.Abort(); return string.Empty; //方法中止返回值 } } public string TestProcdure() { Thread.Sleep(5000); //这个方法要5秒的执行时间 return "SUCCESS"; }
测试代码:
string result = CallWithTimeout(TestProcdure, 4000); //执行时间为4秒,方法中止 result = string.Empty result = CallWithTimeout(TestProcdure, 6000); //执行时间为6秒,方法正常执行 result = "SUCCESS"
注意,Func仅支持4.0以上的版本,要在2.0下使用可以用下面的类。
任意参数类型及参数个数通用版(2.0下亲测成功)
public class FuncTimeout { /// <summary> /// 信号量 /// </summary> public ManualResetEvent manu = new ManualResetEvent(false); /// <summary> /// 是否接受到信号 /// </summary> public bool isGetSignal; /// <summary> /// 设置超时时间 /// </summary> public int timeout; /// <summary> /// 定义一个委托 ,输入参数可选,输出object /// </summary> public delegate object EventNeedRun(params object[] param); /// <summary> /// 要调用的方法的一个委托 /// </summary> private EventNeedRun FunctionNeedRun; /// <summary> /// 构造函数,传入超时的时间以及运行的方法 /// </summary> /// <param name="_action">运行的方法 </param> /// <param name="_timeout">超时的时间</param> public FuncTimeout(EventNeedRun _action, int _timeout) { FunctionNeedRun = _action; timeout = _timeout; } /// <summary> /// 调用函数 /// </summary> /// <param name="input">可选个数的输入参数</param> /// <returns></returns> public object doAction(params object[] input) { EventNeedRun WhatTodo = CombineActionAndManuset; //通过BeginInvoke方法,在线程池上异步的执行方法。 IAsyncResult r = WhatTodo.BeginInvoke(input, MyAsyncCallback, null); //设置阻塞,如果上述的BeginInvoke方法在timeout之前运行完毕,则manu会收到信号。此时isGetSignal为true。 //如果timeout时间内,还未收到信号,即异步方法还未运行完毕,则isGetSignal为false。 isGetSignal = manu.WaitOne(timeout, false); //WaitOne方法在Framework2.0下不支持一个参数的,这里要给两个参数才能兼容2.0的版本。 if (isGetSignal == true) { return WhatTodo.EndInvoke(r); } else { return null; } } /// <summary> /// 把要传进来的方法,和 manu.Set()的方法合并到一个方法体。 /// action方法运行完毕后,设置信号量,以取消阻塞。 /// </summary> /// <param name="input">输入参数</param> /// <returns></returns> public object CombineActionAndManuset(params object[] input) { object output = FunctionNeedRun(input); manu.Set(); return output; } /// <summary> /// 回调函数 /// </summary> /// <param name="ar"></param> public void MyAsyncCallback(IAsyncResult ar) { //isGetSignal为false,表示异步方法其实已经超出设置的时间,此时不再需要执行回调方法。 if (isGetSignal == false) { //放弃执行回调函数; Thread.CurrentThread.Abort(); } } } public object TestProcdure(params object[] param) { Thread.Sleep(5000); //这个方法要5秒的执行时间 return "SUCCESS"; }
测试代码:
FuncTimeout.EventNeedRun action = new FuncTimeout.EventNeedRun(TestProcdure); FuncTimeout ft = new FuncTimeout(action, 4000); string result = (string)ft.doAction(); //执行时间为4秒,方法中止 result = string.Empty ft = new FuncTimeout(action, 6000); result = (string)ft.doAction(); //执行时间为6秒,方法正常执行 result = "SUCCESS"
可以看出来,上面的TestProcdure有些奇怪,返回值为什么用object,为什么有参数。
其实这个测试方法只是为了和FuncTimeout类里面的委托一致,具体使用时可以做相应的调整。