国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

最近用Timer踩了一個坑,分享一下避免別人繼續踩

2019-11-14 16:21:41
字體:
來源:轉載
供稿:網友

 

  最近做一個小項目,項目中有一個定時服務,需要向對方定時發送數據,時間間隔是1.5s,然后就想到了用C#的Timer類,我們知道Timer

確實非常好用,因為里面有非常人性化的start和stop功能,在Timer里面還有一個Interval,就是用來設置時間間隔,然后時間間隔到了就會觸

發Elapsed事件,我們只需要把callback函數注冊到這個事件就可以了,如果Interval到了就會觸發Elapsed,貌似一切看起來很順其自然,但是

有一點一定要注意,callback函數本身執行也是需要時間的,也許這個時間是1s,2s或者更長時間,而timer類卻不管這些,它只顧1.5s觸發一下

Elapsed,這就導致了我的callback可能還沒有執行完,下一個callback又開始執行了,也就導致了沒有達到我預期的1.5s的效果,并且還出現了

一個非常嚴重的問題,那就是線程激增,非常恐怖。

 

   下面舉個例子,為了簡化一下,我就定義一個task任務,當然項目中是多個task任務一起跑的。

 

一:問題產生

   為了具有更高的靈活性,我定義了一個CustomTimer類繼承自Timer,然后里面可以放些Task要跑的數據,這里就定義一個Queue。

 1 namespace Sample 2 { 3     class PRogram 4     { 5         static void Main(string[] args) 6         { 7             TimerCustom timer = new TimerCustom(); 8  9             timer.Interval = 1500;10 11             timer.Elapsed += (obj, evt) =>12             {13                 TimerCustom singleTimer = obj as TimerCustom;14 15                 if (singleTimer != null)16                 {17                     if (singleTimer.queue.Count != 0)18                     {19                         var item = singleTimer.queue.Dequeue();20 21                         Send(item);22                     }23                 }24             };25 26             timer.Start();27 28             Console.Read();29         }30 31         static void Send(int obj)32         {33             //隨機暫定8-10s34             Thread.Sleep(new Random().Next(8000, 10000));35 36             Console.WriteLine("當前時間:{0},定時數據發送成功!", DateTime.Now);37         }38     }39 40     class TimerCustom : System.Timers.Timer41     {42         public Queue<int> queue = new Queue<int>();43 44         public TimerCustom()45         {46             for (int i = 0; i < short.MaxValue; i++)47             {48                 queue.Enqueue(i);49             }50         }51     }52 }

 

二:解決方法

1.  從上圖看,在一個任務的情況下就已經有14個線程了,并且在21s的時候有兩個線程同時執行了,我的第一反應就是想怎么把后續執行callback的

線程踢出去,也就是保證當前僅讓兩個線程在用callback,一個在執行,一個在等待執行,如果第一個線程的callback沒有執行完,后續如果來了第三

個線程的話,我就把這第三個線程直接踢出去,直到第一個callback執行完后,才允許第三個線程進來并等待執行callback,然后曾今的第二個線程開

始執行callback,后續的就以此類推。。。

然后我就想到了用lock機制,在customTimer中增加lockMe,lockNum,isFirst字段,用lockMe來鎖住,用lockNum來踢當前多余的要執行callback

的線程,用isFirst來判斷是不是第一次執行該callback,后續callback的線程必須先等待1.5s再執行。

 1 namespace Sample 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             TimerCustom timer = new TimerCustom(); 8  9             timer.Interval = 1500;10 11             timer.Elapsed += (obj, evt) =>12             {13                 TimerCustom singleTimer = obj as TimerCustom;14 15                 if (singleTimer != null)16                 {17                     //如果當前等待線程>2,就踢掉該線程18                     if (Interlocked.Read(ref singleTimer.lockNum) > 2)19                         return;20 21                     Interlocked.Increment(ref singleTimer.lockNum);22 23                     //這里的lock只能存在一個線程等待24                     lock (singleTimer.lockMe)25                     {26                         if (!singleTimer.isFirst)27                         {28                             Thread.Sleep((int)singleTimer.Interval);29                         }30 31                         singleTimer.isFirst = false;32 33                         if (singleTimer.queue.Count != 0)34                         {35                             var item = singleTimer.queue.Dequeue();36 37                             Send(item);38 39                             Interlocked.Decrement(ref singleTimer.lockNum);40                         }41                     }42                 }43             };44 45             timer.Start();46 47             Console.Read();48         }49 50         static void Send(int obj)51         {52             Thread.Sleep(new Random().Next(8000, 10000));53 54             Console.WriteLine("當前時間:{0},郵件發送成功!", DateTime.Now);55         }56     }57 58     class TimerCustom : System.Timers.Timer59     {60         public Queue<int> queue = new Queue<int>();61 62         public object lockMe = new object();63 64         public bool isFirst = true;65 66         /// <summary>67         /// 為保持連貫性,默認鎖住兩個68         /// </summary>69         public long lockNum = 0;70 71         public TimerCustom()72         {73             for (int i = 0; i < short.MaxValue; i++)74             {75                 queue.Enqueue(i);76             }77         }78     }79 }

 

 

從圖中可以看到,已經沒有同一秒出現重復任務的發送情況了,并且線程也給壓制下去了,乍一看效果不是很明顯,不過這是在一個任務的情況

下的場景,任務越多就越明顯了,所以這個就達到我要的效果。

 

2. 從上面的解決方案來看,其實我們的思維已經被問題約束住了,當時我也是這樣,畢竟坑出來了,就必須來填坑,既然在callback中出現線程

  蜂擁的情況,我當然要想辦法管制了,其實這也沒什么錯,等問題解決了再回頭考慮下時,我們會發現文章開頭說的Timer類有強大的Stop和

   Start功能,所以。。。。這個時候思維就跳出來了,何不在callback執行的時候把Timer關掉,執行完callback后再把Timer開啟,這樣不就

   可以解決問題嗎?好吧,說干就干。

 1 namespace Sample 2 { 3     class Program 4     { 5         static void Main(string[] args) 6         { 7             TimerCustom timer = new TimerCustom(); 8  9             timer.Interval = 1500;10 11             timer.Elapsed += (obj, evt) =>12             {13                 TimerCustom singleTimer = obj as TimerCustom;14 15                 //先停掉16                 singleTimer.Stop();17 18                 if (singleTimer != null)19                 {20                     if (singleTimer.queue.Count != 0)21                     {22                         var item = singleTimer.queue.Dequeue();23 24                         Send(item);25 26                         //發送完成之后再開啟27                         singleTimer.Start();28                     }29                 }30             };31 32             timer.Start();33 34             Console.Read();35         }36 37         static void Send(int obj)38         {39             Thread.Sleep(new Random().Next(8000, 10000));40 41             Console.WriteLine("當前時間:{0},郵件發送成功!", DateTime.Now);42         }43     }44 45     class TimerCustom : System.Timers.Timer46     {47         public Queue<int> queue = new Queue<int>();48 49         public object lockMe = new object();50 51         /// <summary>52         /// 為保持連貫性,默認鎖住兩個53         /// </summary>54         public long lockNum = 0;55 56         public TimerCustom()57         {58             for (int i = 0; i < short.MaxValue; i++)59             {60                 queue.Enqueue(i);61             }62         }63     }64 }

 

從圖中可以看到,問題同樣得到解決,而且更簡單,精妙。


最后總結一下:解決問題的思維很重要,但是如果跳出思維站到更高的抽象層次上考慮問題貌似也很難得。。。

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 威信县| 武乡县| 贵德县| 凌海市| 通江县| 福贡县| 集贤县| 马鞍山市| 哈尔滨市| 连江县| 榆社县| 林甸县| 乌拉特前旗| 宁波市| 开化县| 鹿邑县| 临泽县| 苍山县| 马公市| 灌阳县| 萝北县| 太和县| 洛扎县| 泗水县| 鹤山市| 施秉县| 大同县| 建平县| 五原县| 鲜城| 洛宁县| 长岛县| 喀什市| 三穗县| 合肥市| 合肥市| 巴林左旗| 厦门市| 肥城市| 颍上县| 乌苏市|