使用System.Threading.Timer產生並發的原因和避免並發的小技巧


    本人使用服務調用Timer處理手機端和Pda端上傳至服務器的大量文件,有時會報拒絕訪問文件甚至服務崩潰的情況。因為Timer到期后,線程池線程會執行回調方法,如果回調方法執行時間太長,Timer會再次(在上次執行回調方法結束前)調用線程池新線程執行回調方法。此時,如果回調方法訪問共享資源,我們沒有加同步鎖,就會造成數據的不一致、拒絕訪問、多次處理相同資源甚至程序崩潰的錯誤。如本人的程序,思路是處理完文件A之后將其挪到其它備份查閱目錄,如果第一個線程處理A文件占用了很長時間,另一個線程再次處理A文件,此時第一個線程是不允許挪走A文件的,第二個正在讀取,有可能第三個,第四個甚至更多線程訪問A文件。這樣會造成A文件多次重復處理,向數據庫多次插入同樣的記錄,並且可能鎖死A文件。演示代碼如下:

 nmespace TimerExample
{
    class Program
    {
       static Timer _timer;
        static void Main(string[] args)
        {
            //實例化timer,5秒之后執行,間隔20秒
            _timer = new Timer(DealFile, null, 5 * 1000, 20 * 1000);
            Console.ReadLine();
        }

      // 處理文件的方法會很占用時間
     pivate static void DealFile(object state)
        {
            //通過比對隨機數查看執行情況
            Random random = new Random();
            int tempRandomData= random.Next();
            Console.WriteLine("處理文件開始...," + tempRandomData);
            //模擬處理文件的時間25秒
            Thread.Sleep(25 * 1000);
            Console.WriteLine("處理文件結束...,"+tempRandomData);


         }
  }
}

執行結果如下:

處理文件開始...,217262014
處理文件開始...,1304498158
處理文件結束...,217262014
處理文件開始...,918462592
處理文件結束...,1304498158

由上可知,當第一次執行沒有結束時,Timer再次調用了回調方法DealFile,產生了處理文件開始(綠色部分)的輸出

  果我們使用Timer的意圖是為了定期掃描處理文件,不是為了並發提高速度(我們本來就不想用並發,否則是在Timer回調方法內使用多線程技術,而不會因為回調方法執行時間太長使得Timer自己產生多個線程)。我們可以在首次事例化Timer時,peroid傳入System.Threading.Timeout.Infinite,表示只執行一次timer,然后在回調方法內調用Change方法,timer.Change(interval,System.Threading.Timeout.Infinite),如此則是每次處理完回調方法之后才會再次在Interval時間之后執行回調方法,演示代碼如下

 namespace TimerExample
{
    class Program
    {
       static Timer _timer;
        static void Main(string[] args)
        {
            //實例化timer,5秒之后執行,間隔20秒
            _timer = new Timer(DealFile, null, 5 * 1000, Timeout.Infinite);//20 * 1000);
            Console.ReadLine();
        }
        private static void DealFile(object state)
        {
            //通過比對隨機數查看執行情況
            Random random = new Random();
            int tempRandomData= random.Next();
            Console.WriteLine("處理文件開始...," + tempRandomData);
            //模擬處理文件的時間25秒
            Thread.Sleep(25 * 1000);
            Console.WriteLine("處理文件結束...,"+tempRandomData);
            _timer.Change(20 * 1000, Timeout.Infinite);
        }
    }
}

 執行結果:

處理文件開始...,40380869
處理文件結束...,40380869
處理文件開始...,1750026338
處理文件結束...,1750026338

由上執行可知,Timer的調用是在上次回調方法執行結束之后,才間隔interval(20秒)再次調用回調方法的

當然,此時執行回調方法的時間間隔是interval加上上一次回調方法執行所需時間。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM