C#的Timer解析(转)
?
在C#里現在有3個Timer類:
?System.Windows.Forms.Timer
?System.Threading.Timer
?System.Timers.Timer
這三個Timer我想大家對System.Windows.Forms.Timer已經很熟悉了,唯一我要說的就是這個Timer在激發Timer.Tick事件的時候,事件的處理函數是在程序主線程上執行的,所以在WinForm上面用這個Timer很方便,因為在From上的所有控件都是在程序主線程上創建的,那么在Tick的處理函數中可以對Form上的所有控件進行操作,不會造成WinForm控件的線程安全問題。
1、Timer運行的核心都是System.Threading.ThreadPool
在這里要提到ThreadPool(線程池)是因為,System.Threading.Timer 和System.Timers.Timer運行的核心都是線程池,Timer每到間隔時間后就會激發響應事件,因此要申請線程來執行對應的響應函數,Timer將獲取線程的工作都交給了線程池來管理,每到一定的時間后它就去告訴線程池:“我現在激發了個事件要運行對應的響應函數,麻煩你給我向操作系統要個線程,申請交給你了,線程分配下來了你就運行我給你的響應函數,沒分配下來先讓響應函數在這兒排隊(操作系統線程等待隊列)”,消息已經傳遞給線程池了,Timer也就不管了,因為它還有其他的事要做(每隔一段時間它又要激發事件),至于提交的請求什么時候能夠得到滿足,要看線程池當前的狀態:
?1、如果線程池現在有線程可用,那么申請馬上就可以得到滿足,有線程可用又可以分為兩種情況:
?<1>線程池現在有空閑線程,現在馬上就可以用
?<2>線程池本來現在沒有線程了,但是剛好申請到達的時候,有線程運行完畢釋放了,那么申請就可以用別人釋放的線程。
?這兩種情況情況就如同你去游樂園玩賽車,如果游樂園有10輛車,現在有3個人在玩,那么還剩7輛車,你去了當然可以選一輛開。另外還有一種情況就是你到達游樂園前10輛車都在開,但是你運氣很好,剛到游樂園就有人不玩了,正好你坐上去就可以接著開。
?2、如果現在線程池現在沒有線程可用,也分為兩種情況:
?<1>線程池現有線程數沒有達到設置的最大工作線程數,那么隔半秒鐘.net framework就會向操作系統申請一個新的線程(為避免向線程分配不必要的堆棧空間,線程池按照一定的時間間隔創建新的空閑線程。該時間間隔目前為半秒,但它在 .NET Framework 的以后版本中可能會更改)。
?<2>線程池現有工作線程數達到了設置的最大工作線程數,那么申請只有在等待隊列一直等下去,直到有線程執行完任務后被釋放。
?那么上面提到了線程池有最大工作線程數,其實還有最小空閑線程數,那么這兩個關鍵字是什么意思呢:
?1、最大工作線程數:實際上就是指的線程池能夠向操作系統申請的最大線程數,這個值在.net framework中有默認值,這個默認值是根據你計算機的配置來的,當人你可以用ThreadPool.GetMaxThreads返回線程池當前最大工作線程數,你也可以同ThreadPool.SetMaxThreads設置線程池當前最大工作線程數。
?2、最小空閑線程數:是指在程序開始后,線程池就默認向操作系統要最小空閑線程數個線程,另外這也是線程池維護的空閑線程數(如果線程池最小空閑線程數為3,當前因為一些線程執行完任務被釋放,線程池現在實際上有10個空閑線程,那么線程池會讓操作系統釋放多余的7個線程,而只維持3個空閑線程供程序使用),因為上面說了,在執行程序的時候在要線程池申請線程有半秒的延遲時間,這也會影響程序的性能,所以把握好這個值很重要,用樣你可以用ThreadPool.GetMinThreads返回線程池當前最小空閑線程數,你也可以同ThreadPool.SetMinThreads設置線程池當前最小空閑線程數。
下面是我給的例子,這個例子讓線程池申請800個線程,其中設置最大工作線程數為500,800個線程任務每個都要執行100000000毫秒目的是讓線程不會釋放,并且讓用戶選擇,是否預先申請500個空閑線程免受那半秒鐘的延遲時間,其結果可想而知當線程申請到500的時候,線程池達到了最大工作線程數,剩余的300個申請進入漫長的等待時間:
view plaincopy to clipboardprint?
01./***************************************************?
02. * 項目:測試線程池?
03. * 描述:驗證線程池的最大工作線程數和最小空閑線程數?
04. * 作者:@PowerCoder?
05. * 日期:2010-2-22?
06.***************************************************/?
07.?
08.using System;??
09.using System.Collections.Generic;??
10.using System.Text;??
11.using System.Threading;??
12.?
13.namespace ConsoleApplication1??
14.{??
15.??? class Program??
16.??? {??
17.??????? static int i=1;??
18.??????? static int MaxThreadCount = 800;??
19.?
20.??????? static void OutPut(object obj)??
21.??????? {??
22.??????????? Console.Write("\r申請了:{0}個工作線程",i);??
23.??????????? i++;??
24.??????????? Thread.Sleep(100000000);//設置一個很大的等待時間,讓每個申請的線程都一直執行??
25.??????? }??
26.?
27.??????? static void Main(string[] args)??
28.??????? {??
29.??????????? int j;??
30.??????????????
31.??????????? Console.Write("是否先申請500個空閑線程以保證前500個線程在線程池中開始就有線程用(Y/N)?");//如果這里選擇N,那么前兩個任務是用的線程池默認空閑線程(可以用ThreadPool.GetMinThreads得到系統默認最小空閑線程數為2)申請立即得到滿足,然而由于每個線程等待時間非常大都不會釋放當前自己持有的線程,因此線程池中已無空閑線程所用,后面的任務需要在線程池中申請新的線程,那么新申請的每個線程在線程池中都要隔半秒左右的時間才能得到申請(原因請見下面的注釋)??
32.??????????? string key = Console.ReadLine();??
33.??????????? if(key.ToLower()=="y")??
34.??????????????? ThreadPool.SetMinThreads(500, 10);//設置最大空閑線程為500,就好像我告訴系統給我預先準備500個線程我來了就直接用,因為這樣就不用現去申請了,在線程池中每申請一個新的線程.NET Framework 會安排一個間隔時間,目前是半秒,以后的版本MS有可能會改??
35.??????????????
36.??????????? int a, b;??
37.??????????? ThreadPool.GetMaxThreads(out a,out b);??
38.??????????? Console.WriteLine("線程池默認最大工作線程數:" + a.ToString() + "???? 默認最大異步 I/O 線程數:" + b.ToString());??
39.??????????? Console.WriteLine("需要向系統申請" + MaxThreadCount.ToString()+"個工作線程");??
40.?
41.??????????? for (j = 0; j <= MaxThreadCount-1; j++)//由于ThreadPool.GetMaxThreads返回的默認最大工作線程數為500(這個值要根據你計算機的配置來決定),那么向線程池申請大于500個線程的時候,500之后的線程會進入線程池的等待隊列,等待前面500個線程某個線程執行完后來喚醒等待隊列的某個線程???
42.??????????? {??
43.??????????????? ThreadPool.QueueUserWorkItem(new WaitCallback(OutPut));??
44.??????????????? Thread.Sleep(10);??
45.??????????? }??
46.?
47.??????????? Console.ReadLine();??
48.??????? }??
49.??? }??
50.}?
/***************************************************
?* 項目:測試線程池
?* 描述:驗證線程池的最大工作線程數和最小空閑線程數
?* 作者:@PowerCoder
?* 日期:2010-2-22
***************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
??? class Program
??? {
??????? static int i=1;
??????? static int MaxThreadCount = 800;
??????? static void OutPut(object obj)
??????? {
??????????? Console.Write("\r申請了:{0}個工作線程",i);
??????????? i++;
??????????? Thread.Sleep(100000000);//設置一個很大的等待時間,讓每個申請的線程都一直執行
??????? }
??????? static void Main(string[] args)
??????? {
??????????? int j;
???????????
??????????? Console.Write("是否先申請500個空閑線程以保證前500個線程在線程池中開始就有線程用(Y/N)?");//如果這里選擇N,那么前兩個任務是用的線程池默認空閑線程(可以用ThreadPool.GetMinThreads得到系統默認最小空閑線程數為2)申請立即得到滿足,然而由于每個線程等待時間非常大都不會釋放當前自己持有的線程,因此線程池中已無空閑線程所用,后面的任務需要在線程池中申請新的線程,那么新申請的每個線程在線程池中都要隔半秒左右的時間才能得到申請(原因請見下面的注釋)
??????????? string key = Console.ReadLine();
??????????? if(key.ToLower()=="y")
??????????????? ThreadPool.SetMinThreads(500, 10);//設置最大空閑線程為500,就好像我告訴系統給我預先準備500個線程我來了就直接用,因為這樣就不用現去申請了,在線程池中每申請一個新的線程.NET Framework 會安排一個間隔時間,目前是半秒,以后的版本MS有可能會改
???????????
??????????? int a, b;
??????????? ThreadPool.GetMaxThreads(out a,out b);
??????????? Console.WriteLine("線程池默認最大工作線程數:" + a.ToString() + "???? 默認最大異步 I/O 線程數:" + b.ToString());
??????????? Console.WriteLine("需要向系統申請" + MaxThreadCount.ToString()+"個工作線程");
??????????? for (j = 0; j <= MaxThreadCount-1; j++)//由于ThreadPool.GetMaxThreads返回的默認最大工作線程數為500(這個值要根據你計算機的配置來決定),那么向線程池申請大于500個線程的時候,500之后的線程會進入線程池的等待隊列,等待前面500個線程某個線程執行完后來喚醒等待隊列的某個線程
??????????? {
??????????????? ThreadPool.QueueUserWorkItem(new WaitCallback(OutPut));
??????????????? Thread.Sleep(10);
??????????? }
??????????? Console.ReadLine();
??????? }
??? }
}
2、System.Threading.Timer
談完了線程池,就可以開始討論Timer,這里我們先從System.Threading.Timer開始,System.Threading.Timer的作用就是每到間隔時間后激發響應事件并執行相應函數,執行響應函數要向線程池申請線程,當然申請中會遇到一些情況在上面我們已經說了。值得注意的一點就是System.Threading.Timer在創建對象后立即開始執行,比如System.Threading.Timer timer = new System.Threading.Timer(Excute, null, 0, 10);這句執行完后每隔10毫秒就執行Excute函數不需要啟動什么的。下面就舉個例子,我先把代碼貼出來:
view plaincopy to clipboardprint?
01.using System;??
02.using System.Collections.Generic;??
03.using System.Text;??
04.using System.Threading;??
05.using System.Diagnostics;??
06.?
07.namespace ConsoleApplication1??
08.{??
09.??? class UnSafeTimer??
10.??? {??
11.??????? static int i = 0;??
12.??????? static System.Threading.Timer timer;??
13.??????? static object mylock = new object();??
14.??????? static int sleep;??
15.??????? static bool flag;??
16.??????? public static Stopwatch sw = new Stopwatch();??
17.?
18.??????? static void Excute(object obj)??
19.??????? {??
20.??????????? Thread.CurrentThread.IsBackground = false;??
21.??????????? int c;??
22.?
23.??????????? lock (mylock)??
24.??????????? {??
25.??????????????? i++;??
26.??????????????? c = i;??
27.??????????? }??
28.?
29.??????????? if (c == 80)??
30.??????????? {??
31.??????????????? timer.Dispose();//執行Dispose后Timer就不會再申請新的線程了,但是還是會給Timmer已經激發的事件申請線程??
32.??????????????? sw.Stop();??
33.??????????? }??
34.?
35.??????????? if (c < 80)??
36.??????????????? Console.WriteLine("Now:" + c.ToString());??
37.??????????? else?
38.??????????? {??
39.??????????????? Console.WriteLine("Now:" + c.ToString()+"-----------Timer已經Dispose耗時:"+sw.ElapsedMilliseconds.ToString()+"毫秒");??
40.??????????? }??
41.?
42.??????????? if (flag)??
43.??????????? {??
44.??????????????? Thread.Sleep(sleep);//模擬花時間的代碼??
45.??????????? }??
46.??????????? else?
47.??????????? {??
48.??????????????? if(i<=80)??
49.??????????????????? Thread.Sleep(sleep);//前80次模擬花時間的代碼??
50.??????????? }??
51.??????? }??
52.?
53.??????? public static void Init(int p_sleep,bool p_flag)??
54.??????? {??
55.??????????? sleep = p_sleep;??
56.??????????? flag = p_flag;??
57.??????????? timer = new System.Threading.Timer(Excute, null, 0, 10);??
58.??????? }??
59.??? }??
60.?
61.??? class SafeTimer??
62.??? {??
63.??????? static int i = 0;??
64.??????? static System.Threading.Timer timer;??
65.?
66.??????? static bool flag = true;??
67.??????? static object mylock = new object();??
68.?
69.??????? static void Excute(object obj)??
70.??????? {??
71.??????????? Thread.CurrentThread.IsBackground = false;??
72.?
73.??????????? lock (mylock)??
74.??????????? {??
75.??????????????? if (!flag)??
76.??????????????? {??
77.??????????????????? return;??
78.??????????????? }??
79.?
80.??????????????? i++;??
81.?
82.??????????????? if (i == 80)??
83.??????????????? {??
84.??????????????????? timer.Dispose();??
85.??????????????????? flag = false;??
86.??????????????? }??
87.??????????????? Console.WriteLine("Now:" + i.ToString());??
88.??????????? }??
89.?
90.??????????? Thread.Sleep(1000);//模擬花時間的代碼??
91.??????? }??
92.?
93.??????? public static void Init()??
94.??????? {??
95.??????????? timer = new System.Threading.Timer(Excute, null, 0, 10);??
96.??????? }??
97.??? }??
98.?
99.??? class Program??
100.??? {??
101.??????? static void Main(string[] args)??
102.??????? {??
103.??????????? Console.Write("是否使用安全方法(Y/N)?");??
104.??????????? string key = Console.ReadLine();??
105.??????????? if (key.ToLower() == "y")??
106.??????????????? SafeTimer.Init();??
107.??????????? else?
108.??????????? {??
109.??????????????? Console.Write("請輸入Timmer響應事件的等待時間(毫秒):");//這個時間直接決定了前80個任務的執行時間,因為等待時間越短,每個任務就可以越快執行完,那么80個任務中就有越多的任務可以用到前面任務執行完后釋放掉的線程,也就有越多的任務不必去線程池申請新的線程避免多等待半秒鐘的申請時間??
110.??????????????? string sleep = Console.ReadLine();??
111.??????????????? Console.Write("申請了80個線程后Timer剩余激發的線程請求是否需要等待時間(Y/N)?");//這里可以發現選Y或者N只要等待時間不變,最終Timer激發線程的次數都相近,說明Timer的確在執行80次的Dispose后就不再激發新的線程了??
112.??????????????? key = Console.ReadLine();??
113.??????????????? bool flag = false;??
114.??????????????? if (key.ToLower() == "y")??
115.??????????????? {??
116.??????????????????? flag = true;??
117.??????????????? }??
118.?
119.??????????????? UnSafeTimer.sw.Start();??
120.??????????????? UnSafeTimer.Init(Convert.ToInt32(sleep), flag);??
121.??????????? }??
122.?
123.??????????? Console.ReadLine();??
124.??????? }??
125.??? }??
126.}?
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace ConsoleApplication1
{
??? class UnSafeTimer
??? {
??????? static int i = 0;
??????? static System.Threading.Timer timer;
??????? static object mylock = new object();
??????? static int sleep;
??????? static bool flag;
??????? public static Stopwatch sw = new Stopwatch();
??????? static void Excute(object obj)
??????? {
??????????? Thread.CurrentThread.IsBackground = false;
??????????? int c;
??????????? lock (mylock)
??????????? {
??????????????? i++;
??????????????? c = i;
??????????? }
??????????? if (c == 80)
??????????? {
??????????????? timer.Dispose();//執行Dispose后Timer就不會再申請新的線程了,但是還是會給Timmer已經激發的事件申請線程
??????????????? sw.Stop();
??????????? }
??????????? if (c < 80)
??????????????? Console.WriteLine("Now:" + c.ToString());
??????????? else
??????????? {
??????????????? Console.WriteLine("Now:" + c.ToString()+"-----------Timer已經Dispose耗時:"+sw.ElapsedMilliseconds.ToString()+"毫秒");
??????????? }
??????????? if (flag)
??????????? {
??????????????? Thread.Sleep(sleep);//模擬花時間的代碼
??????????? }
??????????? else
??????????? {
??????????????? if(i<=80)
??????????????????? Thread.Sleep(sleep);//前80次模擬花時間的代碼
??????????? }
??????? }
??????? public static void Init(int p_sleep,bool p_flag)
??????? {
??????????? sleep = p_sleep;
??????????? flag = p_flag;
??????????? timer = new System.Threading.Timer(Excute, null, 0, 10);
??????? }
??? }
??? class SafeTimer
??? {
??????? static int i = 0;
??????? static System.Threading.Timer timer;
??????? static bool flag = true;
??????? static object mylock = new object();
??????? static void Excute(object obj)
??????? {
??????????? Thread.CurrentThread.IsBackground = false;
??????????? lock (mylock)
??????????? {
??????????????? if (!flag)
??????????????? {
??????????????????? return;
??????????????? }
??????????????? i++;
??????????????? if (i == 80)
??????????????? {
??????????????????? timer.Dispose();
??????????????????? flag = false;
??????????????? }
??????????????? Console.WriteLine("Now:" + i.ToString());
??????????? }
??????????? Thread.Sleep(1000);//模擬花時間的代碼
??????? }
??????? public static void Init()
??????? {
??????????? timer = new System.Threading.Timer(Excute, null, 0, 10);
??????? }
??? }
??? class Program
??? {
??????? static void Main(string[] args)
??????? {
??????????? Console.Write("是否使用安全方法(Y/N)?");
??????????? string key = Console.ReadLine();
??????????? if (key.ToLower() == "y")
??????????????? SafeTimer.Init();
??????????? else
??????????? {
??????????????? Console.Write("請輸入Timmer響應事件的等待時間(毫秒):");//這個時間直接決定了前80個任務的執行時間,因為等待時間越短,每個任務就可以越快執行完,那么80個任務中就有越多的任務可以用到前面任務執行完后釋放掉的線程,也就有越多的任務不必去線程池申請新的線程避免多等待半秒鐘的申請時間
??????????????? string sleep = Console.ReadLine();
??????????????? Console.Write("申請了80個線程后Timer剩余激發的線程請求是否需要等待時間(Y/N)?");//這里可以發現選Y或者N只要等待時間不變,最終Timer激發線程的次數都相近,說明Timer的確在執行80次的Dispose后就不再激發新的線程了
??????????????? key = Console.ReadLine();
??????????????? bool flag = false;
??????????????? if (key.ToLower() == "y")
??????????????? {
??????????????????? flag = true;
??????????????? }
??????????????? UnSafeTimer.sw.Start();
??????????????? UnSafeTimer.Init(Convert.ToInt32(sleep), flag);
??????????? }
??????????? Console.ReadLine();
??????? }
??? }
}
?
這個例子包含了兩個Timer的類UnSafeTimer和SafeTimer,兩個類的代碼的大致意思就是使用Timer每隔10毫秒就執行Excute函數,Excute函數會顯示當前執行的次數,在80次的時候通過timer.Dispose()讓Timer停止不再激發響應事件。
首先我們來分析下UnSafeTimer
class UnSafeTimer
{
??? static int i = 0;
??? static System.Threading.Timer timer;
??? static object mylock = new object();
??? static int sleep;
??? static bool flag;
??? public static Stopwatch sw = new Stopwatch();
??? static void Excute(object obj)
??? {
??????? Thread.CurrentThread.IsBackground = false;
??????? int c;
??????? lock (mylock)
??????? {
??????????? i++;
??????????? c = i;
??????? }
??????? if (c == 80)
??????? {
??????????? timer.Dispose();//執行Dispose后Timer就不會再申請新的線程了,但是還是會給Timmer已經激發的事件申請線程
??????????? sw.Stop();
??????? }
??????? if (c < 80)
??????????? Console.WriteLine("Now:" + c.ToString());
??????? else
??????? {
??????????? Console.WriteLine("Now:" + c.ToString() + "-----------Timer已經Dispose耗時:" + sw.ElapsedMilliseconds.ToString() + "毫秒");
??????? }
??????? if (flag)
??????? {
??????????? Thread.Sleep(sleep);//模擬花時間的代碼
??????? }
??????? else
??????? {
??????????? if (i <= 80)
??????????????? Thread.Sleep(sleep);//前80次模擬花時間的代碼
??????? }
??? }
??? public static void Init(int p_sleep, bool p_flag)
??? {
??????? sleep = p_sleep;
??????? flag = p_flag;
??????? timer = new System.Threading.Timer(Excute, null, 0, 10);
??? }
}
你可以執行試一試,在輸入是否執行安全方法的時候選N,等待時間1000,申請了80個線程后Timer剩余激發的線程選N,本來想在80次的時候停下來,可是你會發現直到執行到660多次之后才停下來(具體看機器配置),申請前80個線程的時間為10532毫秒,反正執行的次數大大超出了限制的80次,回頭想想讓Timer不在激發事件的方法是調用timer.Dispose(),難不成是Dispose有延遲?延遲的過程中多執行了500多次?那么我們再來做個試驗,我們在申請了80個線程后Timer剩余激發的線程選y,請耐心等待結果,在最后你會發現執行時間還是660次左右,這很顯然是不合理的,如果Dispose有延遲時間造成所執行500多次,那么加長80次后面每個線程的申請時間在相同的延遲時間內申請的線程數應該減少,因為后面500多個線程每個線程都要執行1000毫秒,那么勢必有些線程會去申請新的線程有半秒鐘的等待時間(你會發現申請了80個線程后Timer剩余激發的線程選y明顯比選n慢得多,就是因為這個原因),所以看來不是因為Dispose造成的。
那么會是什么呢?我們這次這樣選在輸入是否執行安全方法的時候選N,等待時間500,申請了80個線程后Timer剩余激發的線程選N
?
那么會是什么呢?我們這次這樣選在輸入是否執行安全方法的時候選N,等待時間50,申請了80個線程后Timer剩余激發的線程選N
?
我們發現隨著每次任務等待時間的減少多執行的次數也在減少,最關鍵的一點我們從圖中可以看到,前80次任務申請的時間也在減少,這是最關鍵的,根據上面線程池所講的內容我們可以歸納出:每次任務的等待時間直接決定了前80個任務的執行時間,因為等待時間越短,每個任務就可以越快執行完,那么80個任務中就有越多的任務可以用到前面任務執行完后釋放掉的線程,也就有越多的任務不必去線程池申請新的線程避免多等待半秒鐘的申請時間,而Timer并不會去關心線程池申請前80個任務的時間長短,只要它沒有執行到timer.Dispose(),它就會每隔10毫秒激發一次響應時間,不管前80次任務執行時間是長還是短,timer都在第80次任務才執行Dispose,執行Dispose后timer就不會激發新的事件了,但是如果前80次任務申請的時間越長,那么timer就會在前80次任務申請的時間內激發越多響應事件,那么線程池中等待隊列中就會有越多的響應函數等待申請線程,System.Threading.Timer沒有機制取消線程池等待隊列中多余的申請數,所以導致等待時間越長,80次后執行的任務數越多。
由此只用timer.Dispose()來終止Timer激發事件是不安全的,所以又寫了個安全的執行機制:
class SafeTimer
{
??? static int i = 0;
??? static System.Threading.Timer timer;
??? static bool flag = true;
??? static object mylock = new object();
??? static void Excute(object obj)
??? {
??????? Thread.CurrentThread.IsBackground = false;
??????? lock (mylock)
??????? {
??????????? if (!flag)
??????????? {
??????????????? return;
??????????? }
??????????? i++;
??????????? if (i == 80)
??????????? {
??????????????? timer.Dispose();
??????????????? flag = false;
??????????? }
??????????? Console.WriteLine("Now:" + i.ToString());
??????? }
??????? Thread.Sleep(1000);//模擬花時間的代碼
??? }
??? public static void Init()
??? {
??????? timer = new System.Threading.Timer(Excute, null, 0, 10);
??? }
}
安全類中我們用了個bool類型的變量flag來判斷當前是否執行到80次了,執行到80次后將flag置為false,然后timer.Dispose,這時雖然任務還是要多執行很多次但是由于flag為false,Excute函數一開始就做了判斷flag為false會立即退出,Excute函數80次后相當于就不執行了。
3、System.Timers.Timer
在上面的例子中我們看到System.Threading.Timer很不安全,即使在安全的方法類,也只能讓事件響應函數在80次后立刻退出讓其執行時間近似于0,但是還是浪費了系統不少的資源。
所以本人更推薦使用現在介紹的System.Timers.Timer,System.Timers.Timer大致原理和System.Threading.Timer差不多,唯一幾處不同的就是:
?構造函數不同,構造函數可以什么事情也不做,也可以傳入響應間隔時間:System.Timers.Timer timer = new System.Timers.Timer(10);
?響應事件的響應函數不在構造函數中設置:timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
?聲明System.Timers.Timer對象后他不會自動執行,需要調用 timer.Start()或者timer.Enabled = true來啟動它, timer.Start()的內部原理還是設置timer.Enabled = true
?調用 timer.Stop()或者timer.Enabled = false來停止引發Elapsed事件, timer.Stop()的內部原理還是設置timer.Enabled = false,最重要的是timer.Enabled = false后會取消線程池中當前等待隊列中剩余任務的執行。
那么我們來看個例子:
view plaincopy to clipboardprint?
01.using System;??
02.using System.Collections.Generic;??
03.using System.Linq;??
04.using System.Text;??
05.using System.Timers;??
06.using System.Threading;??
07.?
08.namespace ConsoleApplication2??
09.{??
10.??? class UnSafeTimer??
11.??? {??
12.??????? static int i = 0;??
13.??????? static System.Timers.Timer timer;??
14.??????? static object mylock = new object();??
15.?
16.??????? public static void Init()??
17.??????? {??
18.??????????? timer = new System.Timers.Timer(10);??
19.??????????? timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);??
20.??????????? timer.Start();??
21.??????? }??
22.?
23.??????? static void timer_Elapsed(object sender, ElapsedEventArgs e)??
24.??????? {??
25.??????????? Thread.CurrentThread.IsBackground = false;??
26.??????????? int c;??
27.?
28.??????????? lock (mylock)??
29.??????????? {??
30.??????????????? i++;??
31.??????????????? c = i;??
32.??????????? }??
33.?
34.??????????? Console.WriteLine("Now:" + i.ToString());??
35.?
36.??????????? if (c == 80)??
37.??????????? {??
38.??????????????? timer.Stop();//可應看到System.Timers.Timer的叫停機制比System.Threading.Timer好得多,就算在不安全的代碼下Timer也最多多執行一兩次(我在試驗中發現有時會執行到81或82),說明Stop方法在設置Timer的Enable為false后不僅讓Timer不再激發響應事件,還取消了線程池等待隊列中等待獲得線程的任務,至于那多執行的一兩次任務我個人認為是Stop執行過程中會耗費一段時間才將Timer的Enable設置為false,這段時間多余的一兩個任務就獲得了線程開始執行??
39.??????????? }??
40.?
41.?
42.??????????? Thread.Sleep(1000);//等待1000毫秒模擬花時間的代碼,注意:這里的等待時間直接決定了80(由于是不安全模式有時會是81或82、83)個任務的執行時間,因為等待時間越短,每個任務就可以越快執行完,那么80個任務中就有越多的任務可以用到前面任務執行完后釋放掉的線程,也就有越多的任務不必去線程池申請新的線程避免多等待半秒鐘的申請時間??
43.??????? }??
44.??? }??
45.?
46.??? class SafeTimer??
47.??? {??
48.??????? static int i = 0;??
49.??????? static System.Timers.Timer timer;??
50.?
51.??????? static bool flag = true;??
52.??????? static object mylock = new object();??
53.?
54.??????? public static void Init()??
55.??????? {??
56.??????????? timer = new System.Timers.Timer(10);??
57.??????????? timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);??
58.??????????? timer.Start();???
59.??????? }??
60.?
61.??????? static void timer_Elapsed(object sender, ElapsedEventArgs e)??
62.??????? {??
63.??????????? Thread.CurrentThread.IsBackground = false;??
64.?
65.??????????? lock (mylock)??
66.??????????? {??
67.??????????????? if (!flag)??
68.??????????????? {??
69.??????????????????? return;??
70.??????????????? }??
71.??????????????? i++;??
72.?
73.??????????????? Console.WriteLine("Now:" + i.ToString());??
74.?
75.??????????????? if (i == 80)??
76.??????????????? {??
77.??????????????????? timer.Stop();??
78.??????????????????? flag = false;??
79.??????????????? }??
80.??????????? }??
81.?
82.??????????? Thread.Sleep(1000);//同UnSafeTimer??
83.??????? }??
84.?
85.??????? class Program??
86.??????? {??
87.??????????? static void Main(string[] args)??
88.??????????? {??
89.??????????????? Console.Write("是否使用安全Timer>(Y/N)?");??
90.??????????????? string Key = Console.ReadLine();??
91.?
92.??????????????? if (Key.ToLower() == "y")??
93.??????????????????? SafeTimer.Init();??
94.??????????????? else?
95.??????????????????? UnSafeTimer.Init();??
96.?
97.??????????????? Console.ReadLine();??
98.??????????? }??
99.??????? }??
100.??? }??
101.}?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading;
namespace ConsoleApplication2
{
??? class UnSafeTimer
??? {
??????? static int i = 0;
??????? static System.Timers.Timer timer;
??????? static object mylock = new object();
??????? public static void Init()
??????? {
??????????? timer = new System.Timers.Timer(10);
??????????? timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
??????????? timer.Start();
??????? }
??????? static void timer_Elapsed(object sender, ElapsedEventArgs e)
??????? {
??????????? Thread.CurrentThread.IsBackground = false;
??????????? int c;
??????????? lock (mylock)
??????????? {
??????????????? i++;
??????????????? c = i;
??????????? }
??????????? Console.WriteLine("Now:" + i.ToString());
??????????? if (c == 80)
??????????? {
??????????????? timer.Stop();//可應看到System.Timers.Timer的叫停機制比System.Threading.Timer好得多,就算在不安全的代碼下Timer也最多多執行一兩次(我在試驗中發現有時會執行到81或82),說明Stop方法在設置Timer的Enable為false后不僅讓Timer不再激發響應事件,還取消了線程池等待隊列中等待獲得線程的任務,至于那多執行的一兩次任務我個人認為是Stop執行過程中會耗費一段時間才將Timer的Enable設置為false,這段時間多余的一兩個任務就獲得了線程開始執行
??????????? }
??????????? Thread.Sleep(1000);//等待1000毫秒模擬花時間的代碼,注意:這里的等待時間直接決定了80(由于是不安全模式有時會是81或82、83)個任務的執行時間,因為等待時間越短,每個任務就可以越快執行完,那么80個任務中就有越多的任務可以用到前面任務執行完后釋放掉的線程,也就有越多的任務不必去線程池申請新的線程避免多等待半秒鐘的申請時間
??????? }
??? }
??? class SafeTimer
??? {
??????? static int i = 0;
??????? static System.Timers.Timer timer;
??????? static bool flag = true;
??????? static object mylock = new object();
??????? public static void Init()
??????? {
??????????? timer = new System.Timers.Timer(10);
??????????? timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
??????????? timer.Start();
??????? }
??????? static void timer_Elapsed(object sender, ElapsedEventArgs e)
??????? {
??????????? Thread.CurrentThread.IsBackground = false;
??????????? lock (mylock)
??????????? {
??????????????? if (!flag)
??????????????? {
??????????????????? return;
??????????????? }
??????????????? i++;
??????????????? Console.WriteLine("Now:" + i.ToString());
??????????????? if (i == 80)
??????????????? {
??????????????????? timer.Stop();
??????????????????? flag = false;
??????????????? }
??????????? }
??????????? Thread.Sleep(1000);//同UnSafeTimer
??????? }
??????? class Program
??????? {
??????????? static void Main(string[] args)
??????????? {
??????????????? Console.Write("是否使用安全Timer>(Y/N)?");
??????????????? string Key = Console.ReadLine();
??????????????? if (Key.ToLower() == "y")
??????????????????? SafeTimer.Init();
??????????????? else
??????????????????? UnSafeTimer.Init();
??????????????? Console.ReadLine();
??????????? }
??????? }
??? }
}
?這個例子和System.Threading.Timer差不多,這里也分為:安全類SafeTimer和不安全類UnSafeTimer,原因是 timer.Stop()有少許的延遲時間有時任務會執行到81~83,但是就算是不安全方法也就最多多執行幾次,不像System.Threading.Timer多執行上百次...
所以我這里還是推薦大家使用System.Timers.Timer
?
本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/BusyDonkey/archive/2010/02/25/5327665.aspx
?
轉載于:https://www.cnblogs.com/Godblessyou/archive/2011/04/28/2031884.html
總結
以上是生活随笔為你收集整理的C#的Timer解析(转)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 穿越火线的英文名,cf英文名字格式好看的
- 下一篇: 如何优化代码节约系统资源解决重复实例化对