在寫windows服務的時候需要用到多線程跑數據,執行方法中用lock鎖住一段代碼,記錄日志后發現無效,沒起作用。
program 代碼如下:
/// <summary> /// 應用程序的主入口點 /// </summary> static void Main() { Log4Helper.GetInstance().Info("服務啟動"); try { ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); TimeSpan sleepTime = new TimeSpan(1, 0, 0); //休眠一小時 while (true) { Thread.Sleep(sleepTime); } } catch (Exception e) { Log4Helper.GetInstance().Error(e); } } public static void PushCache(object obj) { var search = obj as SearchParam; MessageCacheBll.GetInstance().PushCache(search); }
public class SearchParam
{
/// <summary>
/// 消息類型
/// </summary>
public int MsgType { get; set; }
}
MessageCacheBll類方法如下:
public class MessageCacheBll { private readonly object _lockThread = new object(); #region Instance private static MessageCacheBll _instance; public static MessageCacheBll GetInstance() { return _instance ?? (_instance = new MessageCacheBll()); } #endregion /// <summary> /// 按照消息類型和學段跑數據 /// </summary> /// <param name="search">msgType消息類型</param> public void PushCache(object obj) { var search = (obj as SearchParam) ?? new SearchParam(); try { var sqlWhere = ""; if (search.MsgType == -1) { sqlWhere += " and spm.type in(1,3,4,5,6,100) ";//1系統消息、3消息、4作業、5成績通知、6請假、100成績通知的發件箱用 } else { sqlWhere += string.Format(" and spm.type={0} ", search.MsgType); } List<SpMessageDto> list = new List<SpMessageDto>(); var updateCacheStatusResult = false;//更新緩存狀態結果 //鎖 獲取當前線程要跑的數據 try { lock (_lockThread) { Log4Helper.GetInstance().Info(string.Format("--------lock-開始 threadid{0}--------", Thread.CurrentThread.ManagedThreadId)); list = MessageCacheDal.GetInstance().GetNoCacheMessageList(sqlWhere); if (list != null && list.Count > 0) { var idList = list.Select(t => t.Id).ToList(); updateCacheStatusResult = MessageCacheDal.GetInstance() .UpdateMessageCacheStatus((int)AppEnum.CacheStatus.Caching, idList); } Log4Helper.GetInstance().Info(string.Format("--------lock-結束 threadid{0}--------", Thread.CurrentThread.ManagedThreadId)); } } catch (Exception e) { Log4Helper.GetInstance().Error(e); } } catch (Exception e) { Log4Helper.GetInstance().Error(string.Format("異常 msgType:{0}。", search.MsgType), e); } } } }
日志輸出結果:
2020-01-01 11:36:02,872 服務啟動
2020-01-01 11:36:02,882 --------lock-開始 threadid4--------
2020-01-01 11:36:02,884 --------lock-開始 threadid5--------
2020-01-01 11:36:02,884 --------lock-開始 threadid7--------
2020-01-01 11:36:02,884 --------lock-開始 threadid3--------
2020-01-01 11:36:03,325 --------lock-結束 threadid3--------
2020-01-01 11:36:03,363 --------lock-結束 threadid7--------
2020-01-01 11:36:03,367 --------lock-結束 threadid4--------
2020-01-01 11:36:03,368 --------lock-開始 threadid6--------
2020-01-01 11:36:03,370 --------lock-結束 threadid5--------
2020-01-01 11:36:03,595 --------lock-結束 threadid6--------
解決方案:
方法一:去掉program中的PushCache方法,main方法中直接調用MessageCacheBll.GetInstance().PushCache(search)
示例代碼:
/// <summary> /// 應用程序的主入口點 /// </summary> static void Main() { Log4Helper.GetInstance().Info("服務啟動"); try { ThreadPool.QueueUserWorkItem(MessageCacheBll.GetInstance().PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(MessageCacheBll.GetInstance().PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(MessageCacheBll.GetInstance().PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(MessageCacheBll.GetInstance().PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(MessageCacheBll.GetInstance().PushCache, new SearchParam() { MsgType = 3 }); TimeSpan sleepTime = new TimeSpan(1, 0, 0); //休眠一小時 while (true) { Thread.Sleep(sleepTime); } } catch (Exception e) { Log4Helper.GetInstance().Error(e); } }
日志輸出結果:
2020-01-01 11:52:47,016 服務啟動
2020-01-01 11:52:47,027 --------lock-開始 threadid3--------
2020-01-01 11:52:47,508 --------lock-結束 threadid3--------
2020-01-01 11:52:47,508 --------lock-開始 threadid4--------
2020-01-01 11:52:47,852 --------lock-結束 threadid4--------
2020-01-01 11:52:47,852 --------lock-開始 threadid5--------
2020-01-01 11:52:48,038 --------lock-結束 threadid5--------
2020-01-01 11:52:48,038 --------lock-開始 threadid6--------
2020-01-01 11:52:48,292 --------lock-結束 threadid6--------
2020-01-01 11:52:48,292 --------lock-開始 threadid7--------
2020-01-01 11:52:48,413 --------lock-結束 threadid7--------
方法二:將MessageCacheBll中的PushCache放到program中,lock也放到program中
示例代碼:
static class Program { /// <summary> /// 應用程序的主入口點 /// </summary> static void Main() { Log4Helper.GetInstance().Info("服務啟動"); try { ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); ThreadPool.QueueUserWorkItem(PushCache, new SearchParam() { MsgType = 3 }); TimeSpan sleepTime = new TimeSpan(1, 0, 0); //休眠一小時 while (true) { Thread.Sleep(sleepTime); } } catch (Exception e) { Log4Helper.GetInstance().Error(e); } } private static readonly object _lockThread = new object(); /// <summary> /// 按照消息類型和學段跑數據 /// </summary> /// <param name="search">msgType消息類型</param> public static void PushCache(object obj) { var search = (obj as SearchParam) ?? new SearchParam(); try { var sqlWhere = ""; if (search.MsgType == -1) { sqlWhere += " and spm.type in(1,3,4,5,6,100) "; } else { sqlWhere += string.Format(" and spm.type={0} ", search.MsgType); } List<SpMessageDto> list = new List<SpMessageDto>(); var updateCacheStatusResult = false;//更新緩存狀態結果 //鎖 獲取當前線程要跑的數據 try { lock (_lockThread) { Log4Helper.GetInstance().Info(string.Format("--------lock-開始 threadid{0}--------", Thread.CurrentThread.ManagedThreadId)); list = MessageCacheDal.GetInstance().GetNoCacheMessageList(sqlWhere); if (list != null && list.Count > 0) { var idList = list.Select(t => t.Id).ToList(); updateCacheStatusResult = MessageCacheDal.GetInstance() .UpdateMessageCacheStatus((int)AppEnum.CacheStatus.Caching, idList); } Log4Helper.GetInstance().Info(string.Format("--------lock-結束 threadid{0}--------", Thread.CurrentThread.ManagedThreadId)); } } catch (Exception e) { Log4Helper.GetInstance().Error(e); } } catch (Exception e) { Log4Helper.GetInstance().Error(string.Format("異常 msgType:{0}。", search.MsgType), e); } }
日志輸出記錄:
2020-01-01 11:57:35,878 服務啟動
2020-01-01 11:57:35,888 --------lock-開始 threadid4--------
2020-01-01 11:57:36,586 --------lock-結束 threadid4--------
2020-01-01 11:57:36,586 --------lock-開始 threadid6--------
2020-01-01 11:57:36,789 --------lock-結束 threadid6--------
2020-01-01 11:57:36,789 --------lock-開始 threadid3--------
2020-01-01 11:57:37,186 --------lock-結束 threadid3--------
2020-01-01 11:57:37,186 --------lock-開始 threadid5--------
2020-01-01 11:57:37,279 --------lock-結束 threadid5--------
2020-01-01 11:57:37,279 --------lock-開始 threadid7--------
2020-01-01 11:57:37,394 --------lock-結束 threadid7--------