程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中,函數運行超時的功能的實現

C#中,函數運行超時的功能的實現

編輯:C#入門知識

主要使用BeginInvoke方法和ManualResetEvent類來實現。
BeginInvoke使得函數在線程池上異步運行,運行完成後,調用回調函數。
ManualResetEvent用於同步阻塞。
設計思想如下:
當函數在線程池中的某一線程上異步的運行的時候,ManualResetEvent阻塞當前線程,等待若干時間。
在等候期間,如果異步函數運行完畢,會對ManualResetEvent設置一個信號,使得阻塞的線程得以繼續運行下去。
如果等候超時了,則阻塞的線程也會取消阻塞,繼續運行下去,但是不再理會回調的函數。(即使回調函數仍然被調用),事實上,BeginInvoke創建的線程都是後台線程,這種線程一但所有的前台線程都退出後(其中主線程就是一個前台線程),不管後台線程是否執行完畢,都會結束線程,並退出。因此如果阻塞的主線程完全運行完畢退出,那麼異步運行的線程也會退出,無論是否運行完畢。
語句isGetSignal = manu.WaitOne(timeout);就是阻塞當前線程一段時間。
該語句阻塞期間,不會對isGetSignal賦值,直到阻塞取消後,才會返回一個值給isGetSignal。
當阻塞是因為收到信號而取消的,得到的值是true。
當阻塞是因為超時而取消的,得到的值是false。
整個流程如下:
clipboard

把這些代碼邏輯封裝成一個類。
這個類就接受一個委托和一個超時時間作為構造函數。
把這個委托和 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的時候,方法未超時
A44D75E977694E5EA44DF12EA53445C6

當超時時間設置為1s的時候,方法超時
66AA2245B38746C29D953684881533F1

  1. 上一頁:
  2. 下一頁: