主要使用BeginInvoke方法和ManualResetEvent類來實作。
BeginInvoke使得函數線上程池上異步運作,運作完成後,調用回調函數。
ManualResetEvent用于同步阻塞。
設計思想如下:
當函數線上程池中的某一線程上異步的運作的時候,ManualResetEvent阻塞目前線程,等待若幹時間。
在等候期間,如果異步函數運作完畢,會對ManualResetEvent設定一個信号,使得阻塞的線程得以繼續運作下去。
如果等候逾時了,則阻塞的線程也會取消阻塞,繼續運作下去,但是不再理會回調的函數。(即使回調函數仍然被調用),事實上,BeginInvoke建立的線程都是背景線程,這種線程一但所有的前台線程都退出後(其中主線程就是一個前台線程),不管背景線程是否執行完畢,都會結束線程,并退出。是以如果阻塞的主線程完全運作完畢退出,那麼異步運作的線程也會退出,無論是否運作完畢。
語句isGetSignal = manu.WaitOne(timeout);就是阻塞目前線程一段時間。
該語句阻塞期間,不會對isGetSignal指派,直到阻塞取消後,才會傳回一個值給isGetSignal。
當阻塞是因為收到信号而取消的,得到的值是true。
當阻塞是因為逾時而取消的,得到的值是false。
整個流程如下:
把這些代碼邏輯封裝成一個類。
這個類就接受一個委托和一個逾時時間作為構造函數。
把這個委托和 ManualResetEvent .Set();語句寫在一個方法體内,CombineActionAndManuset,是以CombineActionAndManuset的調用就是實作了方法運作完畢後,設定取消阻塞信号。
封裝後的代碼:
- public class FuncTimeout
- {
- /// <summary>
- /// 信号量
- /// </summary>
- public ManualResetEvent manu = new ManualResetEvent(false);
- /// <summary>
- /// 是否接受到信号
- /// </summary>
- public bool isGetSignal;
- /// <summary>
- /// 設定逾時時間
- /// </summary>
- public int timeout;
- /// <summary>
- /// 要調用的方法的一個委托
- /// </summary>
- public Action<int> FunctionNeedRun;
- /// <summary>
- /// 構造函數,傳入逾時的時間以及運作的方法
- /// </summary>
- /// <param name="_action"></param>
- /// <param name="_timeout"></param>
- public FuncTimeout(Action<int> _action, int _timeout)
- {
- FunctionNeedRun = _action;
- timeout = _timeout;
- }
- /// <summary>
- /// 回調函數
- /// </summary>
- /// <param name="ar"></param>
- public void MyAsyncCallback(IAsyncResult ar)
- {
- //isGetSignal為false,表示異步方法其實已經超出設定的時間,此時不再需要執行回調方法。
- if (isGetSignal == false)
- {
- Console.WriteLine("放棄執行回調函數");
- Thread.CurrentThread.Abort();
- }
- else
- {
- Console.WriteLine("調用回調函數");
- }
- }
- /// <summary>
- /// 調用函數
- /// </summary>
- /// <param name="param1"></param>
- public void doAction(int param1)
- {
- Action<int> WhatTodo = CombineActionAndManuset;
- //通過BeginInvoke方法,線上程池上異步的執行方法。
- var r=WhatTodo.BeginInvoke(param1, MyAsyncCallback, null);
- //設定阻塞,如果上述的BeginInvoke方法在timeout之前運作完畢,則manu會收到信号。此時isGetSignal為true。
- //如果timeout時間内,還未收到信号,即異步方法還未運作完畢,則isGetSignal為false。
- isGetSignal = manu.WaitOne(timeout);
- if (isGetSignal == true)
- {
- Console.WriteLine("函數運作完畢,收到設定信号,異步執行未逾時");
- }
- else
- {
- Console.WriteLine("沒有收到設定信号,異步執行逾時");
- }
- }
- /// <summary>
- /// 把要傳進來的方法,和 manu.Set()的方法合并到一個方法體。
- /// action方法運作完畢後,設定信号量,以取消阻塞。
- /// </summary>
- /// <param name="num"></param>
- public void CombineActionAndManuset(int num)
- {
- FunctionNeedRun(num);
- manu.Set();
- }
- }
測試代碼:
- class Program
- {
- static void Main(string[] args)
- {
- FuncTimeout ft = new FuncTimeout(dosth, 3000);
- ft.doAction(6);
- }
- static void dosth(int num)
- {
- for (int i = 0; i < num; i++)
- {
- Thread.Sleep(500);
- Console.Write(i);
- }
- }
- }
當逾時時間設定為5s的時候,方法未逾時