天天看點

C#中,函數運作逾時的功能的實作

主要使用BeginInvoke方法和ManualResetEvent類來實作。

BeginInvoke使得函數線上程池上異步運作,運作完成後,調用回調函數。

ManualResetEvent用于同步阻塞。

設計思想如下:

當函數線上程池中的某一線程上異步的運作的時候,ManualResetEvent阻塞目前線程,等待若幹時間。

在等候期間,如果異步函數運作完畢,會對ManualResetEvent設定一個信号,使得阻塞的線程得以繼續運作下去。

如果等候逾時了,則阻塞的線程也會取消阻塞,繼續運作下去,但是不再理會回調的函數。(即使回調函數仍然被調用),事實上,BeginInvoke建立的線程都是背景線程,這種線程一但所有的前台線程都退出後(其中主線程就是一個前台線程),不管背景線程是否執行完畢,都會結束線程,并退出。是以如果阻塞的主線程完全運作完畢退出,那麼異步運作的線程也會退出,無論是否運作完畢。

語句isGetSignal = manu.WaitOne(timeout);就是阻塞目前線程一段時間。

該語句阻塞期間,不會對isGetSignal指派,直到阻塞取消後,才會傳回一個值給isGetSignal。

當阻塞是因為收到信号而取消的,得到的值是true。

當阻塞是因為逾時而取消的,得到的值是false。

整個流程如下:

C#中,函數運作逾時的功能的實作

把這些代碼邏輯封裝成一個類。

這個類就接受一個委托和一個逾時時間作為構造函數。

把這個委托和 ManualResetEvent .Set();語句寫在一個方法體内,CombineActionAndManuset,是以CombineActionAndManuset的調用就是實作了方法運作完畢後,設定取消阻塞信号。

封裝後的代碼:

  1. public class FuncTimeout  
  2.    {  
  3.        /// <summary>  
  4.        /// 信号量  
  5.        /// </summary>  
  6.        public ManualResetEvent manu = new ManualResetEvent(false);  
  7.        /// <summary>  
  8.        /// 是否接受到信号  
  9.        /// </summary>  
  10.        public bool isGetSignal;  
  11.        /// <summary>  
  12.        /// 設定逾時時間  
  13.        /// </summary>  
  14.        public int timeout;  
  15.        /// <summary>  
  16.        /// 要調用的方法的一個委托  
  17.        /// </summary>  
  18.        public Action<int> FunctionNeedRun;  
  19.        /// <summary>  
  20.        /// 構造函數,傳入逾時的時間以及運作的方法  
  21.        /// </summary>  
  22.        /// <param name="_action"></param>  
  23.        /// <param name="_timeout"></param>  
  24.        public FuncTimeout(Action<int> _action, int _timeout)  
  25.        {  
  26.            FunctionNeedRun = _action;  
  27.            timeout = _timeout;  
  28.        }  
  29.        /// <summary>  
  30.        /// 回調函數  
  31.        /// </summary>  
  32.        /// <param name="ar"></param>  
  33.        public void MyAsyncCallback(IAsyncResult ar)  
  34.        {  
  35.            //isGetSignal為false,表示異步方法其實已經超出設定的時間,此時不再需要執行回調方法。  
  36.            if (isGetSignal == false)  
  37.            {  
  38.                Console.WriteLine("放棄執行回調函數");  
  39.                Thread.CurrentThread.Abort();  
  40.            }  
  41.            else 
  42.            {  
  43.                Console.WriteLine("調用回調函數");  
  44.            }  
  45.        }  
  46.        /// <summary>  
  47.        /// 調用函數  
  48.        /// </summary>  
  49.        /// <param name="param1"></param>  
  50.        public void doAction(int param1)  
  51.        {  
  52.            Action<int> WhatTodo = CombineActionAndManuset;  
  53.            //通過BeginInvoke方法,線上程池上異步的執行方法。  
  54.            var r=WhatTodo.BeginInvoke(param1, MyAsyncCallback, null);  
  55.            //設定阻塞,如果上述的BeginInvoke方法在timeout之前運作完畢,則manu會收到信号。此時isGetSignal為true。  
  56.            //如果timeout時間内,還未收到信号,即異步方法還未運作完畢,則isGetSignal為false。  
  57.            isGetSignal = manu.WaitOne(timeout);  
  58.            if (isGetSignal == true)  
  59.            {  
  60.                Console.WriteLine("函數運作完畢,收到設定信号,異步執行未逾時");  
  61.            }  
  62.            else 
  63.            {  
  64.                Console.WriteLine("沒有收到設定信号,異步執行逾時");  
  65.            }  
  66.        }  
  67.        /// <summary>  
  68.        /// 把要傳進來的方法,和 manu.Set()的方法合并到一個方法體。  
  69.        /// action方法運作完畢後,設定信号量,以取消阻塞。  
  70.        /// </summary>  
  71.        /// <param name="num"></param>  
  72.        public void CombineActionAndManuset(int num)  
  73.        {  
  74.            FunctionNeedRun(num);  
  75.            manu.Set();  
  76.        }  
  77.    }  

測試代碼:

  1. class Program  
  2.     {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             FuncTimeout ft = new FuncTimeout(dosth, 3000);  
  6.             ft.doAction(6);  
  7.         }  
  8.        static void dosth(int num)  
  9.         {  
  10.             for (int i = 0; i < num; i++)  
  11.             {  
  12.                 Thread.Sleep(500);  
  13.                 Console.Write(i);  
  14.             }  
  15.         }  
  16.     } 

當逾時時間設定為5s的時候,方法未逾時