支持併發的異步日誌

public class Logger {

    // 用於存放寫日誌任務的隊列
    private Queue<Action> _queue;

    // 用於寫日誌的線程
    private Thread _loggingThread;

    // 用於通知是否有新日誌要寫的「信號器」
    private ManualResetEvent _hasNew;

    // 構造函數,初始化。
    private Logger() {
        _queue = new Queue<Action>();
        _hasNew = new ManualResetEvent(false);

        _loggingThread = new Thread(Process);
        _loggingThread.IsBackground = true;
        _loggingThread.Start();
    }

    // 使用單例模式,保持一個Logger對象
    private static readonly Logger _logger = new Logger();
    private static Logger GetInstance() {
        /* 不安全代碼
        lock (locker) {
            if (_logger == null) {
                _logger = new Logger();
            }
        }*/
        return _logger;
    }

    // 處理隊列中的任務
    private void Process() {
        while (true) {
            // 等待接收信號,阻塞線程。
            _hasNew.WaitOne();

            // 接收到信號後,重置「信號器」,信號關閉。
            _hasNew.Reset(); 

            // 因爲隊列中的任務可能在極速地增長,這裏等待是爲了一次能處理更多的任務,減小對隊列的頻繁「進出」操做。
            Thread.Sleep(100);

            // 開始執行隊列中的任務。
            // 因爲執行過程當中還可能會有新的任務,因此不能直接對原來的 _queue 進行操做,
            // 先將_queue中的任務複製一份後將其清空,而後對這份拷貝進行操做。

            Queue<Action> queueCopy;
            lock (_queue) {
                queueCopy = new Queue<Action>(_queue);
                _queue.Clear();
            }

            foreach (var action in queueCopy) {
                action();
            }
        }
    }

    private void WriteLog(string content) {
        lock (_queue) { // todo: 這裏存在線程安全問題,可能會發生阻塞。
            // 將任務加到隊列
            _queue.Enqueue(() => File.AppendAllText("log.txt", content));
        }

        // 打開「信號」
        _hasNew.Set();
    }

    // 公開一個Write方法供外部調用
    public static void Write(string content) {
        // WriteLog 方法只是向隊列中添加任務,執行時間極短,因此使用Task.Run。
        Task.Run(() => GetInstance().WriteLog(content));
    }
}
相關文章
相關標籤/搜索