ThreadStatic
ThreadStatic是C#里的一個特性,它可以讓你的字段在一個線程里有效,但你不能控制這個字段在何時被回收,即如果聲明一個int32的字段為ThreadStatic,然后你為它賦值時為100,那么它什么被恢復成默認值0,我們不得而知,這在開發時,我們可能只有手動將它設為0才行,比較難看,但也沒辦法,誰讓咱們用了ThreadStatic呢,被聲明為ThreadStatic之后,已經證明這個字段是靜態化的,只不過它是被局限在一個線程內的。
Quartz
Quartz是一個任務調度框架,起源于java,它目前被廣泛的使用在各種后臺處理數據的場合,像一些統計數據,推送數據,消息數據等,它可以大大降低前端服務器的并發壓力,并且Quartz的管理界面也有很多,直接nuget安裝即可,在這些產品中最知名的應該就是CrystalQuartz了,它可以在WEB界面中管理咱們的JOB項目!
日志系統Lind.DDD.Logger
Logger本來是Lind框架的一個日志組件,它是最低層的組件,是其它組件的基礎,也被用到其它的業務系統里,而其中一個Quartz組件里,使用Logger時提出了一個問題,就是如何根據job去自動建立日志目錄,讓每個JOB都有自己的目錄,這樣在分析日志時還是很有必要的。
希望看到的結果如圖
測試用的兩個Job
public class Hello_Job : JobBase { protected override void ExcuteJob() { Console.WriteLine("Hello Job方法:" + Thread.CurrentThread.ManagedThreadId); Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info("Hello Job日志!"); } } public class Hi_Job : Lind.DDD.QuartzJob.JobBase { protected override void ExcuteJob() { Console.WriteLine("Hi Job!" + Thread.CurrentThread.ManagedThreadId); Lind.DDD.Logger.LoggerFactory.Instance.Logger_Info("Hi Job!"); } }
JobBase做于所有Job的基類存在,它主要有自己的抽象方法和IJob的接口方法,其中抽象方法由字類Job自己去實現,去實現自己的業務邏輯;而IJob方法由Quartz框架去調用,并在方法中自己調用了抽象方法的內容,大致代碼如下
[DisallowConcurrentExecution()] public abstract class JobBase : IJob { #region IJob 成員 /// <summary> /// Job主方法 /// </summary> /// <param name="context"></param> public void Execute(IJobExecutionContext context) { Lind.DDD.Logger.LoggerFactory.Instance.SetPath(this.GetType().Name); ExcuteJob(); Console.WriteLine(DateTime.Now.ToString() + "{0}這個Job開始執行", context.JobDetail.Key.Name); } #endregion /// <summary> /// Job具體類去實現自己的邏輯 /// </summary> protected abstract void ExcuteJob(); }
日志組件中的字段使用了ThreadStatic
對日志文件分文件夾存儲,主要在日志組件中使用ThreadStatic來實現的,代碼主要如下
/// <summary> /// 每個子類初始時都執行基類這個構造,初始化當前路徑 /// </summary> public LoggerBase() { FileUrl = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LoggerDir"); } /// <summary> /// 日志文件地址 /// 優化級為mvc方案地址,網站方案地址,console程序地址 /// </summary> [ThreadStatic] static protected string FileUrl;
#region ILogger 成員 public void SetPath(string path) { if (!string.IsNullOrWhiteSpace(path)) { FileUrl = FileUrl + "\\" + path; } } #endregion
對于FileLogger這個文件日志實現類來說,它要做的是,在寫完文件流之后,要把FileUrl這個字段從新賦值,因為我們不知道這個字符串什么時候被清空!
lock (objLock)//防治多線程讀寫沖突 { using (System.IO.StreamWriter srFile = new System.IO.StreamWriter(filePath, true)) { srFile.WriteLine(string.Format("{0}{1}{2}" , DateTime.Now.ToString().PadRight(20) , ("[ThreadID:" + Thread.CurrentThread.ManagedThreadId.ToString() + "]").PadRight(14) , message)); srFile.Close(); srFile.Dispose(); } } //清除當前的路徑 FileUrl = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "LoggerDir");
上面的問題,我也是找了很久,因為總是找不到測試不成功的原因,最后想到了ThreadStatic特性的聲明周期,算是找到根源了,呵呵!
建議大家看看C#的《對象的生與死》!
文章列表