好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

支持多线程的日志记录类实现

支持多线程的日志记录类实现

支持多线程的日志记录类实现

概述

主要设计思想是通过一个共享队列,多个输入端能同时非阻塞式的向队列中增加记录信息,输出端能自动及时的把队列中的记录信息输出到控制台或是保存到文件及数据库中。多个输入端互相隔离,采用多线程实现,但考虑到缓存日志信息的是一个共享队列,自然涉及到线程间的同步问题。本文的实现模式是采用操作系统中很经典的生产者/消费者模式。线程间的同步是通过事件信号,同时对共享队列的修改进行加锁保护,避免多个线程同时修改队列。

日志记录类实现

整个实现除了主要的日志记录类,还要定义同步事件类封装用于线程间同步的事件对象,定义日志信息类用于生成日志信息能存于共享队列中。

1. 同步事件类 SyncEvents

该类的定义与使用参照 《如何:对制造者线程和使用者线程进行同步》

     public   class   SyncEvents
    {
          private  EventWaitHandle _newItemEvent;       //  添加新项 
         private  EventWaitHandle _exitThreadEvent;    //  退出线程 
         private   WaitHandle[] _eventArray;

          public   SyncEvents()
        {

            _newItemEvent  =  new  AutoResetEvent( false  );
            _exitThreadEvent  =  new  ManualResetEvent( false  );
            _eventArray  =  new  WaitHandle[ 2  ];
            _eventArray[  0 ] =  _newItemEvent;
            _eventArray[  1 ] =  _exitThreadEvent;
        }

          public   EventWaitHandle ExitThreadEvent
        {
              get  {  return   _exitThreadEvent; }
        }
          public   EventWaitHandle NewItemEvent
        {
              get  {  return   _newItemEvent; }
        }
          public   WaitHandle[] EventArray
        {
              get  {  return   _eventArray; }
        }       
    } 

对新记录的添加使用 AutoResetEvent 类,输出端线程在响应此事件后,此事件能自动重置。将 ManualResetEvent 类用于通知线程退出,该事件被设置后无论是向共享队列中添加日志记录的输入端线程还是从共享队列中取日志记录的输出端线程都能响应此事件,从而正常退出。

2. 日志信息类

共享队列中存放的就是日志信息类的实例对象,可以根据实际需要对此类中的属性进行增加与修改,这并不影响下面将要介绍的日志记录类正常使用。

     public   class   LogInfo
    {
          private   int   _ID;
          public   int   ID
        {
              get  {  return   _ID; }
              set  { _ID =  value; }
        }

          private   string   _CreateTime;
          public   string   CreateTime
        {
              get  {  return   _CreateTime; }
              set  { _CreateTime =  value; }
        }

          private   string   _Content;
          public   string   Content
        {
              get  {  return   _Content; }
              set  { _Content =  value; }
        }
    } 

3. 日志记录类

类中属性与构造函数

     public   class   Logger
    {
          private   static   Logger _logger;
          private   static   object  _lock =  new   object  ();
          private   static   Thread _thread;
          //  日志队列 
         private  Queue<LogInfo>  _queue;
          private   SyncEvents _syncEvents;

          private   Logger()
        {
            _queue  =  new  Queue<LogInfo> ();
            _syncEvents  =  new   SyncEvents();
        }
          //  获取日志记录类实例 
         public   static   Logger GetLogger()
        {
              if  (_logger ==  null  )
            {
                  //  加锁,防止多线程运行时,重复创建。 
                 lock   (_lock)
                {
                      if  (_logger ==  null  )
                    {
                        _logger  =  new   Logger();
                    }
                }
            }

              return   _logger;
        }
    } 

为了保证共享队列唯一,此类实现采用了单例模式,实现方式是通过定义一个静态的自身logger变量,私有化默认的构造函数,提供一个得到Logger实例的GetLogger方法。这样不能通过new直接创建Logger实例,只能通过GetLogger方法获得,在该方法中就可以通过判断是否已创建了Logger实例,如果已创建则返回已有的,从而保证Logger实例的唯一。

添加日志方法

         private   void   AddLog(Object obj)
        {
            LogInfo log  = obj  as   LogInfo;
              if  (!_syncEvents.ExitThreadEvent.WaitOne( 0 ,  false  ))
            {
                  lock   (((ICollection)_queue).SyncRoot)
                {
                    _queue.Enqueue(log);
                    _syncEvents.NewItemEvent.Set();
                    Console.WriteLine(  "  Input thread: add {0} items  "  , log.ID);
                }
            }
            
        }
          ///   <summary> 
         ///   添加日志
          ///   </summary> 
         ///   <param name="log"></param> 
         public   void   Add(LogInfo log)
        {
            Thread t  =  new   Thread(AddLog);
            t.Start(log);
        } 

首先检查“退出线程”事件,因为 WaitOne 使用的第一个参数为零,该方法会立即返回,所以检查该事件的状态不会阻止当前线程。接着往共享队列中添加日志记录并设置“添加新项”事件,此事件设置后会让因共享队列为空而一直在等待的输出线程继续运行,处理共享队列中的新日志记录。 日志添加通过调用Add方法,启动一个新线程运行AddLog方法向共享队列中添加新日志。


日志输出方法

         ///   <summary> 
         ///   日志保存
          ///   </summary> 
         private   void   Save()
        {
              int  flag =  0  ;
              while  (flag >= 0   )
            {
                  if  (_queue.Count ==  0  )
                {
                   flag  =  WaitHandle.WaitAny(_syncEvents.EventArray);
                     if  (flag ==  1  )
                   {
                       flag  = - 1  ;
                   } 
                }
                  lock   (((ICollection)_queue).SyncRoot)
                {
                      if  (_queue.Count >  0  )
                    {
                        LogInfo log  =  _queue.Dequeue();
                        Console.WriteLine(  "  Output Thread: process {0} items  "  , log.ID);
                    }

                }
            }
            
        }
          public   void   Run()
        {
            _thread  =  new   Thread(Save);
            _thread.Start();
        } 

输出线程主要运行的就是日志保存方法,通过while循环逐个处理共享队列中的日志记录。如果队列为空,则线程暂停进入等待状态,等待“添加新项”事件或“退出线程”事件,两个事件只要有一个被设置则线程继续运行,如果是“退出线程”事件,则设置flag为-1,退出循环线程结束,因为只有在队列为空时才等待“退出线程”事件,这样保证线程退出前队列中的所有的日志记录都被处理。 在程序开始处就运行Run方法,会启动一个新线程运行Save方法,这样只要一添加日志就能自动的被处理。


线程结束方法

         public   void   Stop()
        {
            _syncEvents.ExitThreadEvent.Set();
        } 

通过设置“退出线程”事件,让正在运行的输入线程和输出线程都自动结束运行。

4. 使用示例

     class   Program
    {
          static   void  Main( string  [] args)
        {
            Logger logger  =  Logger.GetLogger();
            logger.Run();

              for  ( int  i =  0 ; i <  100 ; i++ )
            {

                LogInfo log  =  new   LogInfo();
                log.ID  =  i;
                logger.Add(log);
                  if  (i ==  50  )
                {
                    logger.Stop();
                }
            }


            Console.ReadLine();
        }
    } 

 这只是个人学习多线程相关知识而简单实现的日志类,功能简单也未做任何优化。如果需要在实际项目中使用日志记录功能,推荐开源的 Nlog

作者: Ljhero
出处: http://ljhero.cnblogs.com/
本作品采用 知识共享署名 3.0 未本地化版本许可协议 进行许可。欢迎转载,演绎或用于商业目的,但是必须保留本文的署名。

 

分类:  【01】C#开发

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于支持多线程的日志记录类实现的详细内容...

  阅读:38次