华南俳烁实业有限公司

考試首頁 | 考試用書 | 培訓(xùn)課程 | 模擬考場 | 考試論壇  
  當(dāng)前位置:編程開發(fā) > DotNET > C# > 文章內(nèi)容
  

C教程:C中Timer使用及解決重入問題

 [ 2017年6月19日 ] 【

  下面顯示一下輸出結(jié)果:

  是不是感覺上面輸出結(jié)果很奇怪,首先是線程1輸出為1,沒有問題,然后隔了2秒后線程1自增1后輸出為2,這就有問題了,中間為什么還出現(xiàn)了線程2的輸出?更奇怪的是線程2剛開始輸出為1,自增1后盡然變成了3!其實這就是重入所導(dǎo)致的問題。別急,咱們分析一下就知道其中的緣由了。

  首先timer啟動計時后,開啟一個線程1執(zhí)行方法,當(dāng)線程1第一次輸出之后,這時線程1休眠了2秒,此時timer并沒有閑著,因為設(shè)置的計時間隔為1秒,當(dāng)在線程1休眠了1秒后,timer又開啟了線程2執(zhí)行方法,線程2才不管線程1是執(zhí)行中還是休眠狀態(tài),所以此時線程2的輸出也為1,因為線程1還在休眠狀態(tài),并沒有自增。然后又隔了1秒,這時發(fā)生同時發(fā)生兩個事件,線程1過了休眠狀態(tài)自增輸出為2,timer同時又開啟一個線程3,線程3輸出的為線程1自增后的值2,又過了1秒,線程2過了休眠狀態(tài),之前的輸出已經(jīng)是2,所以自增后輸出為3,又過了1秒……我都快暈了,大概就是這意思吧,我想表達的就是:一個Timer開啟的線程處理還沒有完成,到了時間,另一Timer還會繼續(xù)進入該方法進行處理。

  那怎么解決這個問題呢?解決方案有三種,下面一一道來,適應(yīng)不同的場景,不過還是推薦最后一種,比較安全。

  ★重入問題解決方案

  1、使用lock(Object)的方法來防止重入,表示一個Timer處理正在執(zhí)行,下一個Timer發(fā)生的時候發(fā)現(xiàn)上一個沒有執(zhí)行完就等待執(zhí)行,適用重入很少出現(xiàn)的場景(具體也沒研究過,可能比較占內(nèi)存吧)。

  代碼跟上面差不多,在觸發(fā)的方法中加入lock,這樣當(dāng)線程2進入觸發(fā)的方法中,發(fā)現(xiàn)已經(jīng)被鎖,會等待鎖中的代碼處理完在執(zhí)行,代碼如下:

  ?

1
2
3
4
5
6
7
8
9
10
11
private static object locko = new object();
 ///
 /// System.Timers.Timer的回調(diào)方法
 ///
 ///
 ///
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 lock (locko)
 { Console.WriteLine(string.Format("線程{0}輸出:{1}, 輸出時間:{2}", t, outPut.ToString(), DateTime.Now)); System.Threading.Thread.Sleep(2000); outPut++; Console.WriteLine(string.Format("線程{0}自增1后輸出:{1},輸出時間:{2}", t, outPut.ToString(), DateTime.Now)); } }

  執(zhí)行結(jié)果:

  2、設(shè)置一個標(biāo)志,表示一個Timer處理正在執(zhí)行,下一個Timer發(fā)生的時候發(fā)現(xiàn)上一個沒有執(zhí)行完就放棄(注意這里是放棄,而不是等待哦,看看執(zhí)行結(jié)果就明白啥意思了)執(zhí)行,適用重入經(jīng)常出現(xiàn)的場景。代碼如下:

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static int inTimer = 0;
///
/// System.Timers.Timer的回調(diào)方法
///
///
///
private static void TimersTimerHandler(object sender, EventArgs args)
{
int t = ++num;
if (inTimer == 0)
{
inTimer = 1;
Console.WriteLine(string.Format("線程{0}輸出:{1}, 輸出時間:{2}", t, outPut.ToString(), DateTime.Now));
System.Threading.Thread.Sleep(2000);
outPut++;
Console.WriteLine(string.Format("線程{0}自增1后輸出:{1},輸出時間:{2}", t, outPut.ToString(), DateTime.Now));
inTimer = 0;
}
}

  執(zhí)行結(jié)果:

  3、在多線程下給inTimer賦值不夠安全,Interlocked.Exchange提供了一種輕量級的線程安全的給對象賦值的方法(感覺比較高上大,也是比較推薦的一種方法),執(zhí)行結(jié)果與方法2一樣,也是放棄執(zhí)行。Interlocked.Exchange用法參考這里。

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static int inTimer = 0;
 ///
 /// System.Timers.Timer的回調(diào)方法
 ///
 ///
 ///
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 if (Interlocked.Exchange(ref inTimer, 1) == 0)
 {
 Console.WriteLine(string.Format("線程{0}輸出:{1}, 輸出時間:{2}", t, outPut.ToString(), DateTime.Now));
 System.Threading.Thread.Sleep(2000);
 outPut++;
 Console.WriteLine(string.Format("線程{0}自增1后輸出:{1},輸出時間:{2}", t, outPut.ToString(), DateTime.Now));
 Interlocked.Exchange(ref inTimer, 0);
 }
 }
執(zhí)行結(jié)果:

  ★總結(jié)

  終于碼完字了,真心不容易啊。寫博客是個挺耗精力的事情,真心佩服那些大牛們筆耕不輟,致敬!在這里稍微總結(jié)一下,timer是一個使用挺簡單的類,拿來即用,這里主要總結(jié)了使用timer時重入問題的解決,以前也沒思考過這個問題,解決方案也挺簡單,在這里列出了三種,不知道還有沒有其他的方式。這里的解決方案同時也適用多線程的重入問題。

本文糾錯】【告訴好友】【打印此文】【返回頂部
將考試網(wǎng)添加到收藏夾 | 每次上網(wǎng)自動訪問考試網(wǎng) | 復(fù)制本頁地址,傳給QQ/MSN上的好友 | 申請鏈接 | 意見留言 TOP
關(guān)于本站  網(wǎng)站聲明  廣告服務(wù)  聯(lián)系方式  站內(nèi)導(dǎo)航  考試論壇
Copyright © 2007-2013 中華考試網(wǎng)(Examw.com) All Rights Reserved
桐梓县| 林西县| 米林县| 珲春市| 铜山县| 会理县| 东兴市| 潍坊市| 中西区| 错那县| 文山县| 仙桃市| 宜兰县| 修武县| 印江| 靖江市| 阳新县| 武山县| 南投县| 巴里| 通化县| 始兴县| 罗甸县| 西宁市| 合山市| 沙坪坝区| 峨眉山市| 荔波县| 内乡县| 安徽省| 敦煌市| 镇远县| 华蓥市| 顺昌县| 岳西县| 图们市| 双桥区| 娱乐| 富川| 五台县| 建德市|