天天看點

C# 高性能寫檔案 —— 隻為挨磚

說到寫檔案,開源項目log4net對于.NET程式員來說恐怕是無人不知,本人一直想寫一個性能高效的日志元件,但能力有限,是以來向大家學習,還望各位仁兄不吝賜教。

小弟寫了一個簡單的寫檔案的元件,經測試可以支撐5000的并發量(5000線程同時寫同一個檔案),再大的沒測試,因為5000已經把CPU幾乎沾滿了。

貼出全部代碼供大家探讨。

項目很簡單,包含4個類:

FileAppender 基礎類,提供寫檔案操作(私有)

IOLock 對讀寫線程鎖的封裝(私有)

Logger 對外提供寫檔案入口(公開)

Log 隻是一個使用Logger的案例(公開)

代碼如下:

using System; using System.IO; using System.Text; namespace Zhuyi.IO { internal class FileAppender : IDisposable { private readonly IOLock io_lock = new IOLock(); private string f_name = string.Empty;//檔案全路徑 private Encoding f_encode = Encoding.Default;//檔案編碼 private FileStream f_stream = null; private StreamWriter writer = null; private bool isAppend = false;//是否為追加模式 private readonly int reTimes = 5;//嘗試讀取次數 //為避免在寫檔案的過程将writer釋放 private readonly object obj_lock = new object(); public FileAppender(string filename) { LastCallTime = DateTime.Now; this.f_name = filename.Replace("/", "//"); CheckDirectory(); } public FileAppender(string filename, Encoding encode) : this(filename) { this.f_encode = encode; } /// <summary> /// 最後通路時間 /// </summary> public DateTime LastCallTime { get; set; } public void CallAddpender(string content, bool append) { if (f_stream == null || isAppend != append) { isAppend = append; Reset(); } io_lock.AcquireWriterLock(); lock (obj_lock) { try { writer.Write(content); writer.Flush(); } finally { io_lock.ReleaseWriterLock(); } } } public void Dispose() { Close(); } private void CheckDirectory() { string dir = f_name.Substring(0, f_name.LastIndexOf("//")); try { if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } } catch (Exception ex) { throw ex; } } private void Reset() { Close(); OpenFile(isAppend); } private void Close() { lock (obj_lock) { if (f_stream != null) { f_stream.Close(); f_stream.Dispose(); } if (writer != null) { writer.Close(); writer.Dispose(); } } } private void OpenFile(bool append) { Exception ex = null; for (int i = 0; i < reTimes; i++) { try { f_stream = new FileStream(f_name, (append ? FileMode.Append : FileMode.Create), (append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.Read); break; } catch (Exception e) { ex = e; } } if (f_stream == null) throw ex; else writer = new StreamWriter(f_stream, f_encode); } } }

using System; namespace Zhuyi.IO { internal sealed class IOLock { private System.Threading.ReaderWriterLock m_lock; public IOLock() { m_lock = new System.Threading.ReaderWriterLock(); } public void AcquireReaderLock() { m_lock.AcquireReaderLock(-1); //System.Threading.Monitor.Enter(this); } public void ReleaseReaderLock() { m_lock.ReleaseReaderLock(); //System.Threading.Monitor.Exit(this); } public void AcquireWriterLock() { m_lock.AcquireWriterLock(-1); //System.Threading.Monitor.Enter(this); } public void ReleaseWriterLock() { m_lock.ReleaseWriterLock(); //System.Threading.Monitor.Exit(this); } } }

using System; using System.Text; using System.Collections.Generic; using System.Threading; namespace Zhuyi.IO { public class Logger : IDisposable { private static Dictionary<string, FileAppender> logLst = new Dictionary<string, FileAppender>(); private const string NewLine = "/r/n"; #region 定時釋放對象 private const double dSleep = 5;//停止通路某檔案5秒後将其釋放 private const int iPeriod = 5000;//計時器執行時間間隔 //定時器,用來定時釋放不再使用的檔案對象 private static readonly Timer timer = new Timer(new TimerCallback(TimerCall), null, iPeriod, iPeriod); private static void TimerCall(object state) { DateTime now = DateTime.Now; foreach (string key in logLst.Keys) { if ((now - logLst[key].LastCallTime).TotalSeconds > dSleep) { logLst[key].Dispose(); } } } #endregion public Logger(string filename) { this.FileName = filename; } public Logger(string filename, Encoding encode) { this.FileName = filename; if (encode != null) this.Encode = encode; } public string FileName { private get; set; } public Encoding Encode { private get; set; } public void Write(string content) { WriteText(content, false); } public void WriteLine(string content) { WriteText(content + NewLine, false); } public void Append(string content) { WriteText(content, true); } public void AppendLine(string content) { WriteText(content + NewLine, true); } private void WriteText(string content, bool append) { string filename = FileName.ToLower(); FileAppender logger = null; lock (logLst) { if (logLst.ContainsKey(filename)) { logger = logLst[filename]; } else { logger = new FileAppender(FileName, Encode != null ? Encode : Encoding.Default); logLst.Add(filename, logger); } } logger.CallAddpender(content, append); logger.LastCallTime = DateTime.Now; } public void Dispose() { string filename = FileName.ToLower(); if (logLst.ContainsKey(filename)) { logLst[filename].Dispose(); logLst.Remove(filename); } } } }

using System; using System.Collections.Generic; using System.Text; namespace Zhuyi.IO { public class Log { private static IDictionary<string, Logger> logLst = new Dictionary<string, Logger>(); public static void Write(string filename, string content, Encoding encode = null) { GetLogger(filename, encode).Write(content); } public static void Append(string filename, string content, Encoding encode = null) { GetLogger(filename, encode).Append(content); } public static void AppendLine(string filename, string content, Encoding encode = null) { GetLogger(filename, encode).AppendLine(content); } private static Logger GetLogger(string filename, Encoding encode) { Logger logger = null; lock (logLst) { if (logLst.ContainsKey(filename.ToLower())) { logger = logLst[filename.ToLower()]; } else { logger = new Logger(filename, encode); logLst.Add(filename.ToLower(), logger); } } return logger; } } }

完整代碼下載下傳:

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

作者:朱會震

繼續閱讀