記錄下來待以后重用,多線程windows服務
工作項配置:

/// <summary> /// 工作項配置 /// </summary> public abstract class ServiceConfig { #region 子類必需實現的抽象屬性 /// <summary> /// 工作項說明 /// </summary> public abstract string Description { get; } /// <summary> /// 工作項是否開啟 /// </summary> public abstract string Enabled { get; } /// <summary> /// 工作項程序集 /// </summary> public abstract string Assembly { get; } /// <summary> /// 工作項執行間隔時間 /// </summary> public abstract int Interval { get; } #endregion #region 擴展屬性 //可擴展 #endregion }
工作項:

/// <summary> /// 工作項 /// </summary> public abstract class ServiceJob { //配置對象 private ServiceConfig mConfigObject; //下次運行時間 private DateTime mNextTime; //任務是否在運行中 protected bool mIsRunning; /// <summary> /// 構造函數 /// </summary> public ServiceJob() { //變量初始化 this.mNextTime = DateTime.Now; this.mIsRunning = false; } /// <summary> /// 配置對象 /// </summary> public ServiceConfig ConfigObject { get { return this.mConfigObject; } set { this.mConfigObject = value; } } /// <summary> /// 開始工作 /// </summary> public void StartJob() { if (this.mConfigObject != null && this.mNextTime != null) { if (this.mConfigObject.Enabled.ToLower() == "true") { if (DateTime.Now >= this.mNextTime) { if (!this.mIsRunning) { this.mNextTime = DateTime.Now.AddSeconds((double)this.mConfigObject.Interval); this.Start(); } } } } } /// <summary> /// 停止工作 /// </summary> public void StopJob() { this.mConfigObject = null; this.mNextTime = DateTime.Now; this.mIsRunning = false; this.Stop(); } #region 子類必需實現的抽象成員 /// <summary> /// 開始工作 /// </summary> protected abstract void Start(); /// <summary> /// 停止工作 /// </summary> protected abstract void Stop(); #endregion }
工具類:

/// <summary> /// 工具類 /// </summary> public class ServiceTools : System.Configuration.IConfigurationSectionHandler { /// <summary> /// 獲取AppSettings節點值 /// </summary> /// <param name="key"></param> /// <returns></returns> public static string GetAppSetting(string key) { return ConfigurationManager.AppSettings[key].ToString(); } /// <summary> /// 獲取configSections節點 /// </summary> /// <returns></returns> public static XmlNode GetConfigSections() { XmlDocument doc = new XmlDocument(); doc.Load(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath); return doc.DocumentElement.FirstChild; } /// <summary> /// 獲取section節點 /// </summary> /// <param name="nodeName"></param> /// <returns></returns> public static NameValueCollection GetSection(string nodeName) { return (NameValueCollection)ConfigurationManager.GetSection(nodeName); } /// <summary> /// 停止Windows服務 /// </summary> /// <param name="serviceName">服務名稱</param> public static void WindowsServiceStop(string serviceName) { System.ServiceProcess.ServiceController control = new System.ServiceProcess.ServiceController(serviceName); control.Stop(); control.Dispose(); } /// <summary> /// 寫日志 /// </summary> /// <param name="path">日志文件</param> /// <param name="cont">日志內容</param> /// <param name="isAppend">是否追加方式</param> public static void WriteLog(string path, string cont, bool isAppend) { using (StreamWriter sw = new StreamWriter(path, isAppend, System.Text.Encoding.UTF8)) { sw.WriteLine(DateTime.Now); sw.WriteLine(cont); sw.WriteLine(""); sw.Close(); } } /// <summary> /// 實現接口以讀寫app.config /// </summary> /// <param name="parent"></param> /// <param name="configContext"></param> /// <param name="section"></param> /// <returns></returns> public object Create(object parent, object configContext, System.Xml.XmlNode section) { System.Configuration.NameValueSectionHandler handler = new System.Configuration.NameValueSectionHandler(); return handler.Create(parent, configContext, section); } }
Service:

public partial class Service1 : ServiceBase { //用哈希表存放任務項 private Hashtable hashJobs; public Service1() { InitializeComponent(); } protected override void OnStart(string[] args) { WriteLog.WriteMessage("Info", "runJobs_啟動服務_" + DateTime.Now.ToString()); //啟動服務 this.runJobs(); } protected override void OnStop() { WriteLog.WriteMessage("Info", "stopJobs_停止服務_" + DateTime.Now.ToString()); //停止服務 this.stopJobs(); } #region 自定義方法 private void runJobs() { try { WriteLog.WriteMessage("Info", "runJobs_加載工作項_" + DateTime.Now.ToString()); //加載工作項 if (this.hashJobs == null) { hashJobs = new Hashtable(); //獲取configSections節點 XmlNode configSections = Base.ServiceTools.GetConfigSections(); foreach (XmlNode section in configSections) { //過濾注釋節點(如section中還包含其它節點需過濾) if (section.Name.ToLower() == "section") { //創建每個節點的配置對象 string sectionName = section.Attributes["name"].Value.Trim(); string sectionType = section.Attributes["type"].Value.Trim(); //程序集名稱 string assemblyName = sectionType.Split(',')[1]; //完整類名 string classFullName = assemblyName + ".Jobs." + sectionName + ".Config"; //創建配置對象 Base.ServiceConfig config = (Base.ServiceConfig)Assembly.Load(assemblyName).CreateInstance(classFullName); //創建工作對象 Base.ServiceJob job = (Base.ServiceJob)Assembly.Load(config.Assembly.Split(',')[1]).CreateInstance(config.Assembly.Split(',')[0]); job.ConfigObject = config; //將工作對象加載進HashTable this.hashJobs.Add(sectionName, job); } } } WriteLog.WriteMessage("Info", "runJobs_執行工作項_" + DateTime.Now.ToString() + " hashJobs.Keys.Count:" + this.hashJobs.Keys.Count); //執行工作項 if (this.hashJobs.Keys.Count > 0) { foreach (Base.ServiceJob job in hashJobs.Values) { //插入一個新的請求到線程池 if (System.Threading.ThreadPool.QueueUserWorkItem(threadCallBack, job)) { //方法成功排入隊列 WriteLog.WriteMessage("Info", "runJobs_方法成功排入隊列_" + DateTime.Now.ToString() + " Description:" + job.ConfigObject.Description); } else { //方法排入隊列失敗 WriteLog.WriteMessage("Info", "runJobs_方法排入隊列失敗_" + DateTime.Now.ToString() + " Description:" + job.ConfigObject.Description); } } } } catch (Exception error) { WriteLog.WriteErorrLog("Error", error); } } private void stopJobs() { //停止 if (this.hashJobs != null) { this.hashJobs.Clear(); } } /// <summary> /// 線程池回調方法 /// </summary> /// <param name="state"></param> private void threadCallBack(Object state) { while (true) { ((Base.ServiceJob)state).StartJob(); //休眠1秒 Thread.Sleep(1000); } } #endregion }
AppConfig:

<configSections> <!--自定義工作項,name屬性請與Jobs下的任務目錄同名,會據此加載該任務的config對象--> <section name="JobIndustry" type="SouMaiService.Base.ServiceTools,SouMaiService"/> <section name="JobKey" type="SouMaiService.Base.ServiceTools,SouMaiService"/> </configSections> <JobKey> <add key="description" value="關鍵字緩存"/> <add key="enabled" value="true"/> <add key="assembly" value="SouMaiService.Jobs.JobKey.Job,SouMaiService"/> <add key="interval" value="345600000"/> </JobKey> <appSettings> <!--每個線程操作緩存的數據量 --> <add key="ThreadMaxCount" value="30000"/> <!--JobKey 線程的起始值 1 --> <add key="JobKeyStartID" value="2600000"/> <!--JobKey 線程的最大值 當此值為空值時 則表示不配置線程最大值 --> <add key="JobKeyEndID" value="2900000"/> </appSettings>
應用程序的主入口點:

static class Program { /// <summary> /// 應用程序的主入口點。 /// </summary> static void Main() { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new Service1() }; ServiceBase.Run(ServicesToRun); } }
WriteLog日志:

public class WriteLog { /// <summary> /// /// </summary> /// <param name="fileName">文件名</param> /// <param name="ex"></param> public static void WriteErorrLog(string fileName, Exception ex) { if (ex == null) return; //ex = null 返回 DateTime dt = DateTime.Now; // 設置日志時間 string time = dt.ToString("yyyy-MM-dd HH:mm:ss"); //年-月-日 時:分:秒 string logName = dt.ToString("yyyy-MM-dd"); //日志名稱 string logPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, Path.Combine("log", fileName)); //日志存放路徑 string log = Path.Combine(logPath, string.Format("{0}.log", logName)); //路徑 + 名稱 try { FileInfo info = new FileInfo(log); if (info.Directory != null && !info.Directory.Exists) { info.Directory.Create(); } using (StreamWriter write = new StreamWriter(log, true, Encoding.GetEncoding("utf-8"))) { write.WriteLine(time); write.WriteLine(ex.Message); write.WriteLine("異常信息:" + ex); write.WriteLine("異常堆棧:" + ex.StackTrace); write.WriteLine("異常簡述:" + ex.Message); write.WriteLine("\r\n----------------------------------\r\n"); write.Flush(); write.Close(); write.Dispose(); } } catch { } } /// <summary> /// /// </summary> /// <param name="fileName">文件名</param> /// <param name="message"></param> public static void WriteMessage(string fileName, string message) { //ex = null 返回 DateTime dt = DateTime.Now; // 設置日志時間 string time = dt.ToString("yyyy-MM-dd HH:mm:ss"); //年-月-日 時:分:秒 string logName = dt.ToString("yyyy-MM-dd"); //日志名稱 string logPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, Path.Combine("log", fileName)); //日志存放路徑 string log = Path.Combine(logPath, string.Format("{0}.log", logName)); //路徑 + 名稱 try { FileInfo info = new FileInfo(log); if (info.Directory != null && !info.Directory.Exists) { info.Directory.Create(); } using (StreamWriter write = new StreamWriter(log, true, Encoding.GetEncoding("utf-8"))) { write.WriteLine(time); write.WriteLine("信息:" + message); write.WriteLine("\r\n----------------------------------\r\n"); write.Flush(); write.Close(); write.Dispose(); } } catch { } } public static void WriteErorrLog(Exception ex, string message) { if (ex == null) return; //ex = null 返回 DateTime dt = DateTime.Now; // 設置日志時間 string time = dt.ToString("yyyy-MM-dd HH:mm:ss"); //年-月-日 時:分:秒 string logName = dt.ToString("yyyy-MM-dd"); //日志名稱 string logPath = System.AppDomain.CurrentDomain.BaseDirectory; //日志存放路徑 string log = Path.Combine(Path.Combine(logPath, "log"), string.Format("{0}.log", logName)); //路徑 + 名稱 try { FileInfo info = new FileInfo(log); if (info.Directory != null && !info.Directory.Exists) { info.Directory.Create(); } using (StreamWriter write = new StreamWriter(log, true, Encoding.GetEncoding("utf-8"))) { write.WriteLine(time); write.WriteLine(ex.Message); write.WriteLine("異常信息:" + ex); write.WriteLine("異常堆棧:" + ex.StackTrace); write.WriteLine("異常簡述:" + message); write.WriteLine("\r\n----------------------------------\r\n"); write.Flush(); write.Close(); write.Dispose(); } } catch { } } public static void WriteMessage(string message) { //ex = null 返回 DateTime dt = DateTime.Now; // 設置日志時間 string time = dt.ToString("yyyy-MM-dd HH:mm:ss"); //年-月-日 時:分:秒 string logName = dt.ToString("yyyy-MM-dd"); //日志名稱 string logPath = System.AppDomain.CurrentDomain.BaseDirectory; //日志存放路徑 string log = Path.Combine(Path.Combine(logPath, "log"), string.Format("{0}.log", logName)); //路徑 + 名稱 try { FileInfo info = new FileInfo(log); if (info.Directory != null && !info.Directory.Exists) { info.Directory.Create(); } using (StreamWriter write = new StreamWriter(log, true, Encoding.GetEncoding("utf-8"))) { write.WriteLine(time); write.WriteLine("信息:" + message); write.WriteLine("\r\n----------------------------------\r\n"); write.Flush(); write.Close(); write.Dispose(); } } catch { } } }
線程使用:

namespace SouMaiService.Jobs.JobKey { public class Config : Base.ServiceConfig { #region 基本屬性 private string mDescription; private string mEnabled; private string mAssembly; private int mInterval; /// <summary> /// 說明 /// </summary> public override string Description { get { return this.mDescription; } } /// <summary> /// 是否開啟 /// </summary> public override string Enabled { get { return this.mEnabled; } } /// <summary> /// 處理程序集 /// </summary> public override string Assembly { get { return this.mAssembly; } } /// <summary> /// 間隔時間 /// </summary> public override int Interval { get { return this.mInterval; } } #endregion #region 構造函數 /// <summary> /// 構造函數,將配置項加載進對象 /// </summary> public Config() { NameValueCollection nvc = Base.ServiceTools.GetSection("JobKey"); foreach (string s in nvc.Keys) { switch (s.ToLower()) { //基本 case "description": this.mDescription = nvc[s].ToString(); break; case "enabled": this.mEnabled = nvc[s].ToString(); break; case "assembly": this.mAssembly = nvc[s].ToString(); break; case "interval": this.mInterval = int.Parse(nvc[s].ToString()); break; } } } #endregion } }

using System; using System.Collections.Generic; using System.IO; using System.Text; namespace SouMaiService.Jobs.JobKey { public class Job : Base.ServiceJob { /// <summary> /// 任務開始 /// </summary> protected override void Start() { try { //運行中 this.mIsRunning = true; //執行工作項 this.executeLogic(); } catch (Exception error) { //異常日志 WriteLog.WriteErorrLog("JobKey" + DateTime.Now.ToString("yyyy_MM_dd"), error); //發生異常時停止服務程序 Base.ServiceTools.WindowsServiceStop("SouMaiService"); } finally { //空閑 this.mIsRunning = false; } } /// <summary> /// 任務停止 /// </summary> protected override void Stop() { this.mIsRunning = false; } /// <summary> /// 執行邏輯 /// </summary> private void executeLogic() { InsertRedis bll = new InsertRedis(); int minKeyID = int.Parse(Base.ServiceTools.GetAppSetting("JobKeyStartID"));//起始ID int count = 0; //因數據可被刪除 所以需要取出來最大的 KeyID - 起始值 count = bll.GetMaxKeyId() - minKeyID; //count = bll.GetKeyCount(); //每次線程緩存的數據間隔最大值 int maxCount = int.Parse(Base.ServiceTools.GetAppSetting("ThreadMaxCount")); //向上取整 int num = int.Parse(Math.Ceiling((double)count / (double)maxCount).ToString()); int preNum = minKeyID; for (int i = 1; i <= num; i++) { object ids = preNum.ToString() + "^" + (preNum + maxCount).ToString(); //線程池 System.Threading.ThreadPool.QueueUserWorkItem(SetKeyRedis, ids); //bll.SetKeyRedis(preNum, preNum + maxCount); preNum = preNum + maxCount; } } private void SetKeyRedis(object ids) { InsertRedis bll = new InsertRedis(); int beginId = int.Parse(ids.ToString().Split('^')[0]); int endId = int.Parse(ids.ToString().Split('^')[1]); bll.SetKeyRedis(beginId, endId); } } }