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回收。

總結

到此這篇關於C#中定時任務被阻塞問題解決的文章就介紹到這瞭,更多相關C#定時任務被阻塞內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: