• C# 高性能写文件 —— 只为挨砖


    说到写文件,开源项目log4net对于.NET程序员来说恐怕是无人不知,本人一直想写一个性能高效的日志组件,但能力有限,因此来向大家学习,还望各位仁兄不吝赐教。

    小弟写了一个简单的写文件的组件,经测试可以支撑5000的并发量(5000线程同时写同一个文件),再大的没测试,因为5000已经把CPU几乎沾满了。

    贴出全部代码供大家探讨。

    项目很简单,包含4个类:

    FileAppender 基础类,提供写文件操作(私有)

    IOLock 对读写线程锁的封装(私有)

    Logger 对外提供写文件入口(公开)

    Log 只是一个使用Logger的案例(公开)

    代码如下:

    View Code
    1 using System;
    2  using System.IO;
    3  using System.Text;
    4
    5  namespace Zhuyi.IO
    6 {
    7 internal class FileAppender : IDisposable
    8 {
    9 private readonly IOLock io_lock = new IOLock();
    10
    11 private string f_name = string.Empty;//文件全路径
    12   private Encoding f_encode = Encoding.Default;//文件编码
    13  
    14 private FileStream f_stream = null;
    15 private StreamWriter writer = null;
    16
    17 private bool isAppend = false;//是否为追加模式
    18   private readonly int reTimes = 5;//尝试读取次数
    19
    20 //为避免在写文件的过程将writer释放
    21   private readonly object obj_lock = new object();
    22
    23 public FileAppender(string filename)
    24 {
    25 LastCallTime = DateTime.Now;
    26
    27 this.f_name = filename.Replace("/", "\\");
    28
    29 CheckDirectory();
    30 }
    31 public FileAppender(string filename, Encoding encode)
    32 : this(filename)
    33 {
    34 this.f_encode = encode;
    35 }
    36 /// <summary>
    37 /// 最后访问时间
    38 /// </summary>
    39   public DateTime LastCallTime { get; set; }
    40
    41 public void CallAddpender(string content, bool append)
    42 {
    43 if (f_stream == null || isAppend != append)
    44 {
    45 isAppend = append;
    46
    47 Reset();
    48 }
    49 io_lock.AcquireWriterLock();
    50 lock (obj_lock)
    51 {
    52 try
    53 {
    54 writer.Write(content);
    55 writer.Flush();
    56 }
    57 finally
    58 {
    59 io_lock.ReleaseWriterLock();
    60 }
    61 }
    62 }
    63 public void Dispose()
    64 {
    65 Close();
    66 }
    67
    68 private void CheckDirectory()
    69 {
    70 string dir = f_name.Substring(0, f_name.LastIndexOf("\\"));
    71 try
    72 {
    73 if (!Directory.Exists(dir))
    74 {
    75 Directory.CreateDirectory(dir);
    76 }
    77 }
    78 catch (Exception ex)
    79 {
    80 throw ex;
    81 }
    82 }
    83
    84 private void Reset()
    85 {
    86 Close();
    87 OpenFile(isAppend);
    88 }
    89 private void Close()
    90 {
    91 lock (obj_lock)
    92 {
    93 if (f_stream != null)
    94 {
    95 f_stream.Close();
    96 f_stream.Dispose();
    97 }
    98 if (writer != null)
    99 {
    100 writer.Close();
    101 writer.Dispose();
    102 }
    103 }
    104 }
    105
    106 private void OpenFile(bool append)
    107 {
    108 Exception ex = null;
    109 for (int i = 0; i < reTimes; i++)
    110 {
    111 try
    112 {
    113 f_stream = new FileStream(f_name,
    114 (append ? FileMode.Append : FileMode.Create),
    115 (append ? FileAccess.Write : FileAccess.ReadWrite),
    116 FileShare.Read);
    117 break;
    118 }
    119 catch (Exception e) { ex = e; }
    120 }
    121 if (f_stream == null)
    122 throw ex;
    123 else
    124 writer = new StreamWriter(f_stream, f_encode);
    125 }
    126
    127 }
    128 }
    View Code
    1 using System;
    2
    3 namespace Zhuyi.IO
    4 {
    5 internal sealed class IOLock
    6 {
    7 private System.Threading.ReaderWriterLock m_lock;
    8
    9 public IOLock()
    10 {
    11 m_lock = new System.Threading.ReaderWriterLock();
    12 }
    13
    14 public void AcquireReaderLock()
    15 {
    16 m_lock.AcquireReaderLock(-1);
    17
    18 //System.Threading.Monitor.Enter(this);
    19 }
    20
    21 public void ReleaseReaderLock()
    22 {
    23 m_lock.ReleaseReaderLock();
    24
    25 //System.Threading.Monitor.Exit(this);
    26 }
    27
    28 public void AcquireWriterLock()
    29 {
    30 m_lock.AcquireWriterLock(-1);
    31
    32 //System.Threading.Monitor.Enter(this);
    33 }
    34
    35 public void ReleaseWriterLock()
    36 {
    37 m_lock.ReleaseWriterLock();
    38
    39 //System.Threading.Monitor.Exit(this);
    40 }
    41
    42 }
    43 }
    View Code
    1 using System;
    2 using System.Text;
    3 using System.Collections.Generic;
    4 using System.Threading;
    5
    6 namespace Zhuyi.IO
    7 {
    8 public class Logger : IDisposable
    9 {
    10 private static Dictionary<string, FileAppender> logLst = new Dictionary<string, FileAppender>();
    11 private const string NewLine = "\r\n";
    12
    13 #region 定时释放对象
    14 private const double dSleep = 5;//停止访问某文件5秒后将其释放
    15 private const int iPeriod = 5000;//计时器执行时间间隔
    16 //定时器,用来定时释放不再使用的文件对象
    17 private static readonly Timer timer = new Timer(new TimerCallback(TimerCall), null, iPeriod, iPeriod);
    18
    19 private static void TimerCall(object state)
    20 {
    21 DateTime now = DateTime.Now;
    22 foreach (string key in logLst.Keys)
    23 {
    24 if ((now - logLst[key].LastCallTime).TotalSeconds > dSleep)
    25 {
    26 logLst[key].Dispose();
    27 }
    28 }
    29 }
    30 #endregion
    31
    32 public Logger(string filename)
    33 {
    34 this.FileName = filename;
    35 }
    36 public Logger(string filename, Encoding encode)
    37 {
    38 this.FileName = filename;
    39 if (encode != null)
    40 this.Encode = encode;
    41 }
    42
    43 public string FileName { private get; set; }
    44 public Encoding Encode { private get; set; }
    45
    46 public void Write(string content)
    47 {
    48 WriteText(content, false);
    49 }
    50 public void WriteLine(string content)
    51 {
    52 WriteText(content + NewLine, false);
    53 }
    54 public void Append(string content)
    55 {
    56 WriteText(content, true);
    57 }
    58 public void AppendLine(string content)
    59 {
    60 WriteText(content + NewLine, true);
    61 }
    62
    63 private void WriteText(string content, bool append)
    64 {
    65 string filename = FileName.ToLower();
    66 FileAppender logger = null;
    67 lock (logLst)
    68 {
    69 if (logLst.ContainsKey(filename))
    70 {
    71 logger = logLst[filename];
    72 }
    73 else
    74 {
    75 logger = new FileAppender(FileName, Encode != null ? Encode : Encoding.Default);
    76
    77 logLst.Add(filename, logger);
    78 }
    79 }
    80 logger.CallAddpender(content, append);
    81
    82 logger.LastCallTime = DateTime.Now;
    83 }
    84 public void Dispose()
    85 {
    86 string filename = FileName.ToLower();
    87 if (logLst.ContainsKey(filename))
    88 {
    89 logLst[filename].Dispose();
    90 logLst.Remove(filename);
    91 }
    92 }
    93
    94 }
    95 }
    View Code
    1 using System;
    2 using System.Collections.Generic;
    3 using System.Text;
    4
    5 namespace Zhuyi.IO
    6 {
    7 public class Log
    8 {
    9 private static IDictionary<string, Logger> logLst = new Dictionary<string, Logger>();
    10
    11 public static void Write(string filename, string content, Encoding encode = null)
    12 {
    13 GetLogger(filename, encode).Write(content);
    14 }
    15
    16 public static void Append(string filename, string content, Encoding encode = null)
    17 {
    18 GetLogger(filename, encode).Append(content);
    19 }
    20 public static void AppendLine(string filename, string content, Encoding encode = null)
    21 {
    22 GetLogger(filename, encode).AppendLine(content);
    23 }
    24
    25 private static Logger GetLogger(string filename, Encoding encode)
    26 {
    27 Logger logger = null;
    28 lock (logLst)
    29 {
    30 if (logLst.ContainsKey(filename.ToLower()))
    31 {
    32 logger = logLst[filename.ToLower()];
    33 }
    34 else
    35 {
    36 logger = new Logger(filename, encode);
    37 logLst.Add(filename.ToLower(), logger);
    38 }
    39 }
    40 return logger;
    41 }
    42 }
    43 }

    完整代码下载:

    https://files.cnblogs.com/sqzhuyi/Zhuyi.IO.rar

    请高手指点哪里需要改进。

  • 相关阅读:
    (转)IntelliJ IDEA 插件 阿里巴巴Java开发手册(Alibaba Java Coding Guidelines)
    idea快捷键整理
    (转)mysql使用Navicat 导出和导入数据库
    (转)Intellij Idea工具栏添加打开选中文件的资源管理器位置
    Intellij IDEA设置类注释和方法注释
    mavn jar包依赖冲突解决
    我的Keras使用总结(3)——利用bottleneck features进行微调预训练模型VGG16
    我的Keras使用总结(2)——构建图像分类模型(针对小数据集)
    我的Keras使用总结(1)——Keras概述与常见问题整理
    数据竞赛实战(5)——方圆之外
  • 原文地址:https://www.cnblogs.com/sqzhuyi/p/io.html
Copyright © 2020-2023  润新知