解決一個C#中定時任務被阻塞問題

解決一個C#中定時任務被阻塞問題

1.摘要

本文會介紹一個C#中最簡單定時任務的使用方法,以及會遇到的定時任務被阻塞現象,從筆者理解的角度分析原因。以及提供解決方案。

2.C#中定時任務的最簡方法

  protected internal void PollClient()
  {
      int i=0;
      Timer t = new Timer(p => {
          i++;
          if (deviceContextList.Count > 0)
          {
              var deviceContext=GetDeviceContext("123456789");
                  SendMessage(messageList[i%7],deviceContext.tcpSession.writerContext);
              logger.Info("客戶端數量:"+ deviceContextList.Count);        
          }
          else 
          {
              logger.Info("客戶端數量爲0");
              Console.WriteLine("客戶端數量爲0");
          }              
      }, null, 0, 1000) ;           
  }

上面的timer方法提供於微軟System.Threading命名空間。System.Threading.Timer 是由線程池調用的。所有的Timer對象只使用了一個線程來管理。這個線程知道下一個回調對象在什麼時候到期。下一個回調對象到期時,線程就會喚醒,在內部調用ThreadPool 的 QueueUserWorkItem,將一個工作項添加到線程池隊列中,使你的回調方法得到調用。此方法有多個重載,具體讀者可以自行去看。

Timer(TimerCallback callback, object state, int dueTime, int period)

第一個參數callback是回調方法,第二個參數state可以傳參給回調方法的參數,第三個參數dueTime是第一次執行回調函數的延時時間,單位毫秒,第四個參數period是調用回調函數的時間間隔。使用起來是不是特別方便,把你需要執行的定時任務放在回調方法中,可獨立寫成方法,也可像上面一樣寫成匿名方法的形式。

3.定時任務阻塞現象

當上述任務被執行了幾千次以後,定時任務會阻塞,不再執行,也不再打印日誌。並且上面的寫法有缺陷,。如果回調方法的執行時間很長,計時器可能(在上個回調還沒有完成的時候)再次觸發。這可能造成多個線程池線程同時執行你的回調方法。並且線程切換也會造成諸多損耗時間。

4.阻塞現象原因分析

上面的方法中使用局部變量來創建指向一個線程定時器。因爲局部變量會被GC回收,導致定時器失效。
具體改進如下:

static int i=0;
static Timer _timer = null;
        protected  void PollClient()
        {
             _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite) ;           
        }
    private void TimerCallback(object state)
    {
            try
            {
                i++;
              
                if (deviceContextList.Count > 0)
                {
                    var deviceContext = GetDeviceContext("123456789");
                    SendMessage(messageList[i % 7], deviceContext.tcpSession.writerContext);
                    logger.Info("客戶端數量:" + deviceContextList.Count + "循環次數:" + i);

                }
                else
                {
                    logger.Info("客戶端數量爲0" + "循環次數:" + i);
                    Console.WriteLine("客戶端數量爲0" + "循環次數:" + i);
                }
            }
            catch (Exception e)
            {
                logger.Error("定時測試下發報文異常:" + e);
            }
            finally
            {
                _timer.Change( 1000, Timeout.Infinite);
            }
        }

將定時器與計數變量設置爲static是爲了定時器不被GC回收。定時任務執行完成之後再設置下次調用時間間隔是爲了該任務不過多佔用線程池中的線程,節省線程切換時間等。

5.問題解決

可以看到任務已經被執行了86665次,優化後不再被GC回收。


版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。 本文鏈接:https://www.cnblogs.com/JerryMouseLi/p/15543495.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章