Thread.Sleep引起ThreadAbortException異常

短信平臺記錄日誌模塊,是經過異步方式來記錄的,即日誌工具類裏初始化一個Queue對象,公共的寫日誌方法的處理邏輯是把日誌消息放到Queue裏。構造器裏設定一個死循環,不停的讀隊,而後把日誌消息持久化到磁盤的文本文件裏。html

構造方法的代碼以下:異步

public Log()
{
            //開啓線程負責將日誌寫入到指定地點
            ThreadPool.QueueUserWorkItem(o =>
            {
                try
                {
                    while (true)
                    {
                        if (_messageQueue.Count > 0)
                        {
                            lock (_messageQueue)
                            {
                                while (_messageQueue.Count > 0)
                                {
                                    LogMessage logMessage = _messageQueue.Dequeue();
                                    WriteFile(logMessage);
                                }
                            }
                        }
                        else
                        {
                            Thread.Sleep(300);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogMessage log = new LogMessage() { Content = "構造器執行報錯:" + ex.ToString() };
                    FileHelper.WriteFile(string.Format(@"{0}\{1:yyyy-MM-dd}-LOGERROR.txt", _logPath, DateTime.Now), log.GetFormattedLog());
                }
            });
}

 

近期經過看線上站點日誌,發現不按期會有ThreadAbortException被捕獲,分佈式

時間:2016-08-02 10:23:01
構造器執行報錯:System.Threading.ThreadAbortException: 正在停止線程。
   在 System.Threading.Thread.SleepInternal(Int32 millisecondsTimeout)
   在 Common.Log.<.ctor>b__1(Object o)
-------------------------------------------------------------------------------
時間:2016-08-02 10:47:32
構造器執行報錯:System.Threading.ThreadAbortException: 正在停止線程。
   在 System.Threading.Thread.SleepInternal(Int32 millisecondsTimeout)
   在 Common.Log.<.ctor>b__2(Object o)
----------------------------------------------------------------------------------------

 

經過分析,當站點應用程序池回收或遇到其餘未捕獲的異常時,線程會被Abort,這時,當線程裏的代碼再被執行時,就會拋出ThreadAbortException異常。模塊化

 

微軟官方對ThreadAbortException的介紹:工具

在調用 Abort 方法以銷燬線程時,CLR將引起ThreadAbortExceptionThreadAbortException 是一種可捕獲的特殊異常,但在 catch 塊的結尾處它將自動被再次引起。引起此異常時,運行庫將在結束線程前執行全部 finally 塊。因爲線程能夠在 finally 塊中執行未綁定計算,或調用 Thread.ResetAbort 來取消停止,因此不能保證線程將徹底結束。若是您但願一直等到被停止的線程結束,能夠調用 Thread.Join 方法。Join 是一個模塊化調用,它直到線程實際中止執行時才返回。性能

下面的示例說明如何停止線程。接收 ThreadAbortException 的線程使用 ResetAbort 方法取消停止請求並繼續執行。spa

using System;
using System.Threading;
using System.Security.Permissions;

public class ThreadWork {
    public static void DoWork() {
        try {
            for(int i=0; i<100; i++) {
                Console.WriteLine("Thread - working."); 
                Thread.Sleep(100);
            }
        }
        catch(ThreadAbortException e) {
            Console.WriteLine("Thread - caught ThreadAbortException - resetting.");
            Console.WriteLine("Exception message: {0}", e.Message);
            Thread.ResetAbort();
        }
        Console.WriteLine("Thread - still alive and working."); 
        Thread.Sleep(1000);
        Console.WriteLine("Thread - finished working.");
    }
}

class ThreadAbortTest {
    public static void Main() {
        ThreadStart myThreadDelegate = new ThreadStart(ThreadWork.DoWork);
        Thread myThread = new Thread(myThreadDelegate);
        myThread.Start();
        Thread.Sleep(100);
        Console.WriteLine("Main - aborting my thread.");
        myThread.Abort();
        myThread.Join();
        Console.WriteLine("Main ending."); 
    }
}

 

運行結果:線程

Thread - working.
Main - aborting my thread.
Thread - caught ThreadAbortException - resetting.
Exception message: Thread was being aborted.
Thread - still alive and working.
Thread - finished working.
Main ending.

————————————————————–我是萌萌噠分界線————————————————————日誌

生產者/消費者模式
咱們系統裏的日誌Helper類用到了Queue來實現異步記日誌,提升系統容錯和性能。如今想來,這就是一個生產者/消費者模式的運用。
在分佈式系統中,這個模式更適用,尤爲是消息處理。 RabbitMQ正是基於這種需求而產生的消息中間件。
相關文章
相關標籤/搜索