設置調用方法的超時時間


在工作中,遇到這樣一個需求,我要做一個業務,要驗證一下現有的數據是否正確,但這個驗證又不是必須的,只是說如果這里驗證不通過,后面流程就可以不走了,但是如果這里沒有驗證到,后面也會有驗證。也就是說不影響主流程,算得上是一個優化吧。比如我要查詢一個東西,但是這個時間不能超過1秒。

在網上查了一下,基本上都是異步執行,有兩個線程來做。我查到有兩種方法。

第一種 獨立成一個類

代碼如下:

(1)、FuncTimeOut類

    /// <summary>
    /// 超時設置類
    /// </summary>
    public class FuncTimeOut
    {
        /// <summary>
        /// 信號量
        /// </summary>
        private ManualResetEvent manu = new ManualResetEvent(false);

        /// <summary>
        /// 是否接受到信號
        /// </summary>
        private bool isgetSignal;

        /// <summary>
        /// 設置超時時間
        /// </summary>
        private int timeout;

        /// <summary>
        /// 要委托調用的方法的一個委托
        /// </summary>
        private Action<int> funcNeedRun;

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="action">委托</param>
        /// <param name="timeout">超時時間</param>
        public FuncTimeOut(Action<int> action, int timeout)
        {
            this.funcNeedRun = action;
            this.timeout = timeout;
        }

        /// <summary>
        /// 執行方法
        /// </summary>
        /// <param name="param">參數</param>
        public void Execute(int param)
        {
            Action<int> tempAction = this.CombineActionAndManuset;
            var r = tempAction.BeginInvoke(param, this.MyAsynCallback, null);
            this.isgetSignal = this.manu.WaitOne(this.timeout);
            if (this.isgetSignal == true)
            {
                Console.WriteLine("未超時.");
                Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
            }
            else
            {
                Console.WriteLine("超時");
                Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
            }
        }

        /// <summary>
        /// 回調函數
        /// </summary>
        /// <param name="ar">異步操作時的狀態</param>
        private void MyAsynCallback(IAsyncResult ar)
        {
            if (this.isgetSignal == false)
            {
                Console.WriteLine(Thread.CurrentThread.Name + ",超時,放棄執行回調函數");
                Thread.CurrentThread.Abort();
            }
            else
            {
                Console.WriteLine(Thread.CurrentThread.Name + ",執行成功");
            }
        }

        /// <summary>
        /// 執行方法
        /// </summary>
        /// <param name="para">參數</param>
        private void CombineActionAndManuset(int para)
        {
            Thread.CurrentThread.Name = "subThread";
            this.funcNeedRun(para);
            this.manu.Set();
        }
    }

(2)、測試代碼

    /// <summary>
    /// Class Program
    /// </summary>
    public class Program
    {
        /// <summary>
        /// Defines the entry point of the application.
        /// </summary>
        /// <param name="args">The args.</param>
        public static void Main(string[] args)
        {
            TestFuncTimeOut();
            Console.Read();
        }

        #region FunTimeOut測試

        /// <summary>
        /// 測試超時設置操作
        /// </summary>
        private static void TestFuncTimeOut()
        {
            Console.WriteLine("start");
            Thread.CurrentThread.Name = "Main";
            FuncTimeOut ft = new FuncTimeOut(ComputeSum, 3000);
            ft.Execute(10); // 測試修改點
            Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
            Console.WriteLine("end");
        }

        /// <summary>
        /// Does the STH.
        /// </summary>
        /// <param name="num">The num.</param>
        private static void ComputeSum(int num)
        {
            int sum = 0;
            for (int i = 0; i < num; i++)
            {
                Thread.Sleep(500);
                sum += i;
                Console.WriteLine(i + ":ThreadName:" + Thread.CurrentThread.Name);
            }

            Console.WriteLine("sum = " + sum);

            // return sum;
        }

        #endregion
   }

(3)、執行結果

a、超時的情況(上面的測試代碼測出來的結果)

 1 start
 2 0:ThreadName:subThread
 3 1:ThreadName:subThread
 4 2:ThreadName:subThread
 5 3:ThreadName:subThread
 6 4:ThreadName:subThread
 7 超時
 8 ThreadName:Main
 9 ThreadName:Main
10 end
11 5:ThreadName:subThread
12 6:ThreadName:subThread
13 7:ThreadName:subThread
14 8:ThreadName:subThread
15 9:ThreadName:subThread
16 sum = 45
17 subThread,超時,放棄執行回調函數

b、未超時的情況(將上述黃色部分的標示改為:ft.Execute(3);的結果)

 1 start
 2 0:ThreadName:subThread
 3 1:ThreadName:subThread
 4 2:ThreadName:subThread
 5 sum = 3
 6 未超時.
 7 ThreadName:Main
 8 ThreadName:Main
 9 end
10 subThread,執行成功

(4)、總結

從上面的結果可以看出:

第一、有兩個線程

第二、無論是否超時,方法都會被執行完畢,如果超時,則不忘下執行回調函數,否則執行。

第二種 封裝成一個方法

(1)、源代碼

        /// <summary>
        /// 連續整數求和【測試方法】
        /// </summary>
        /// <param name="num">個數</param>
        /// <returns>結果</returns>
        private static int ComputeSumResult(int num)
        {
            int sum = 0;
            for (int i = 0; i < num; i++)
            {
                Thread.Sleep(500);
                sum += i;
                Console.WriteLine(i + ",ThreadName:" + Thread.CurrentThread.Name);
            }

            return sum;
        }

        /// <summary>
        /// 超時方法調用
        /// </summary>
        /// <param name="func">要調用的方法</param>
        /// <param name="param">要調用方法的參數</param>
        /// <param name="timeoutMillisecondes">超時時間</param>
        /// <returns>結束</returns>
        private static int CallWithTimeOutFun(Func<int, int> func, int param, int timeoutMillisecondes)
        {
            Thread threadToKill = null;
            int sum = 0;
            Action wrappedAction = () =>
            {
                threadToKill = Thread.CurrentThread;
                threadToKill.Name = "threadToKill";
                sum = func(param);
            };
            IAsyncResult result = wrappedAction.BeginInvoke(null, null);
            if (result.AsyncWaitHandle.WaitOne(timeoutMillisecondes))
            {
                Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
                wrappedAction.EndInvoke(result);
                Console.WriteLine("沒有超時");
                Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
                return sum;
            }
            else
            {
                Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
                threadToKill.Abort();
                Console.WriteLine("超時");
                Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name);
                return sum;
            }
        }

(2)、測試代碼

    /// <summary>
    /// Class Program
    /// </summary>
    public class Program
    {
        /// <summary>
        /// Defines the entry point of the application.
        /// </summary>
        /// <param name="args">The args.</param>
        public static void Main(string[] args)
        {
            Console.WriteLine("start");
            Thread.CurrentThread.Name = "Main";
            int result = CallWithTimeOutFun(ComputeSumResult, 10, 3000);// 測試需要修改的代碼
            Console.WriteLine("ThreadName:" + Thread.CurrentThread.Name + ", 結果是:" + result);
            Console.WriteLine("end");
            Console.Read();
        }
   }

(3)、測試結果

a、超時情況

 1 start
 2 0,ThreadName:threadToKill
 3 1,ThreadName:threadToKill
 4 2,ThreadName:threadToKill
 5 3,ThreadName:threadToKill
 6 4,ThreadName:threadToKill
 7 ThreadName:Main
 8 超時
 9 ThreadName:Main
10 ThreadName:Main, 結果是:0
11 end

b、不超時的情況(將上面測試需要修改的代碼修改為:int result = CallWithTimeOutFun(ComputeSumResult, 3, 3000);// 測試需要修改的代碼)

1 start
2 0,ThreadName:threadToKill
3 1,ThreadName:threadToKill
4 2,ThreadName:threadToKill
5 ThreadName:Main
6 沒有超時
7 ThreadName:Main
8 ThreadName:Main, 結果是:3
9 end

(4)、總結

從上面的結果可以看出:

第一、有兩個線程

第二、如果方法超時,則不會將被調用的方法執行完畢,可以看出,方法是執行了,但由於超時,結果是不對的。如果方法沒有超時,結果為正確的。這與上面方法的區別在於,方法超時就不會繼續執行了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM