主要使用BeginInvoke方法和ManualResetEvent類來實(shí)現(xiàn)。
成都創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),安達(dá)企業(yè)網(wǎng)站建設(shè),安達(dá)品牌網(wǎng)站建設(shè),網(wǎng)站定制,安達(dá)網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,安達(dá)網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
BeginInvoke使得函數(shù)在線程池上異步運(yùn)行,運(yùn)行完成后,調(diào)用回調(diào)函數(shù)。
ManualResetEvent用于同步阻塞。
設(shè)計(jì)思想如下:
當(dāng)函數(shù)在線程池中的某一線程上異步的運(yùn)行的時(shí)候,ManualResetEvent阻塞當(dāng)前線程,等待若干時(shí)間。
在等候期間,如果異步函數(shù)運(yùn)行完畢,會(huì)對(duì)ManualResetEvent設(shè)置一個(gè)信號(hào),使得阻塞的線程得以繼續(xù)運(yùn)行下去。
如果等候超時(shí)了,則阻塞的線程也會(huì)取消阻塞,繼續(xù)運(yùn)行下去,但是不再理會(huì)回調(diào)的函數(shù)。(即使回調(diào)函數(shù)仍然被調(diào)用),事實(shí)上,BeginInvoke創(chuàng)建的線程都是后臺(tái)線程,這種線程一但所有的前臺(tái)線程都退出后(其中主線程就是一個(gè)前臺(tái)線程),不管后臺(tái)線程是否執(zhí)行完畢,都會(huì)結(jié)束線程,并退出。因此如果阻塞的主線程完全運(yùn)行完畢退出,那么異步運(yùn)行的線程也會(huì)退出,無論是否運(yùn)行完畢。
語句isGetSignal = manu.WaitOne(timeout);就是阻塞當(dāng)前線程一段時(shí)間。
該語句阻塞期間,不會(huì)對(duì)isGetSignal賦值,直到阻塞取消后,才會(huì)返回一個(gè)值給isGetSignal。
當(dāng)阻塞是因?yàn)槭盏叫盘?hào)而取消的,得到的值是true。
當(dāng)阻塞是因?yàn)槌瑫r(shí)而取消的,得到的值是false。
整個(gè)流程如下:
把這些代碼邏輯封裝成一個(gè)類。
這個(gè)類就接受一個(gè)委托和一個(gè)超時(shí)時(shí)間作為構(gòu)造函數(shù)。
把這個(gè)委托和 ManualResetEvent .Set();語句寫在一個(gè)方法體內(nèi),CombineActionAndManuset,因此CombineActionAndManuset的調(diào)用就是實(shí)現(xiàn)了方法運(yùn)行完畢后,設(shè)置取消阻塞信號(hào)。
封裝后的代碼:
- public class FuncTimeout
- {
- ///
- /// 信號(hào)量
- ///
- public ManualResetEvent manu = new ManualResetEvent(false);
- ///
- /// 是否接受到信號(hào)
- ///
- public bool isGetSignal;
- ///
- /// 設(shè)置超時(shí)時(shí)間
- ///
- public int timeout;
- ///
- /// 要調(diào)用的方法的一個(gè)委托
- ///
- public Action
FunctionNeedRun; - ///
- /// 構(gòu)造函數(shù),傳入超時(shí)的時(shí)間以及運(yùn)行的方法
- ///
- ///
- ///
- public FuncTimeout(Action
_action, int _timeout) - {
- FunctionNeedRun = _action;
- timeout = _timeout;
- }
- ///
- /// 回調(diào)函數(shù)
- ///
- ///
- public void MyAsyncCallback(IAsyncResult ar)
- {
- //isGetSignal為false,表示異步方法其實(shí)已經(jīng)超出設(shè)置的時(shí)間,此時(shí)不再需要執(zhí)行回調(diào)方法。
- if (isGetSignal == false)
- {
- Console.WriteLine("放棄執(zhí)行回調(diào)函數(shù)");
- Thread.CurrentThread.Abort();
- }
- else
- {
- Console.WriteLine("調(diào)用回調(diào)函數(shù)");
- }
- }
- ///
- /// 調(diào)用函數(shù)
- ///
- ///
- public void doAction(int param1)
- {
- Action
WhatTodo = CombineActionAndManuset; - //通過BeginInvoke方法,在線程池上異步的執(zhí)行方法。
- var r=WhatTodo.BeginInvoke(param1, MyAsyncCallback, null);
- //設(shè)置阻塞,如果上述的BeginInvoke方法在timeout之前運(yùn)行完畢,則manu會(huì)收到信號(hào)。此時(shí)isGetSignal為true。
- //如果timeout時(shí)間內(nèi),還未收到信號(hào),即異步方法還未運(yùn)行完畢,則isGetSignal為false。
- isGetSignal = manu.WaitOne(timeout);
- if (isGetSignal == true)
- {
- Console.WriteLine("函數(shù)運(yùn)行完畢,收到設(shè)置信號(hào),異步執(zhí)行未超時(shí)");
- }
- else
- {
- Console.WriteLine("沒有收到設(shè)置信號(hào),異步執(zhí)行超時(shí)");
- }
- }
- ///
- /// 把要傳進(jìn)來的方法,和 manu.Set()的方法合并到一個(gè)方法體。
- /// action方法運(yùn)行完畢后,設(shè)置信號(hào)量,以取消阻塞。
- ///
- ///
- public void CombineActionAndManuset(int num)
- {
- FunctionNeedRun(num);
- manu.Set();
- }
- }
測(cè)試代碼:
- 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);
- }
- }
- }
當(dāng)超時(shí)時(shí)間設(shè)置為5s的時(shí)候,方法未超時(shí)
當(dāng)超時(shí)時(shí)間設(shè)置為1s的時(shí)候,方法超時(shí)