代碼是在多線程環境下,做了簡單的Key是否存的判斷, 測試代碼如下:
public class PRogram { static Dictionary<string, Logger> loggreDic; static object loggerDicLocker = new object(); public static void Main() { loggreDic = new Dictionary<string, Logger>(); for (int i = 0; i < 100; i++) { ThreadPool.QueueUserWorkItem(o => { try { var logger = GetLogger("AAA"); } catch (Exception) { Console.WriteLine(string.Format("弟{0}個線程出現問題", o)); } }, i); } Console.ReadKey(); } static Logger GetLogger(string cmdId) { if (!loggreDic.ContainsKey(cmdId)) { loggreDic.Add(cmdId, LogManager.GetLogger(string.Format("ChinaPnrApi.{0}", cmdId))); } return loggreDic[cmdId]; } }
可以看到在GetLogger的地方做了判斷的處理,但是在多線程的時候還是會出現在取的時候取不到的問題??梢詤⒖枷旅娼貓D :
從錯誤異常很容易判斷,是在Dictionary中加了重復的Key造成的.
所以總體上來看這段代碼所犯的問題是不是線程安全的代碼.
1. 使用Locker解決
2. 使用線程安全的
下面對兩種方式都做了實現:
public interface IGetLogger { Logger GetLogger(string cmdId); } public class ConcurrentDictionaryLogger : IGetLogger { ConcurrentDictionary<string, Logger> loggreDic = new ConcurrentDictionary<string, Logger>(); public Logger GetLogger(string cmdId) { if (!loggreDic.ContainsKey(cmdId)) { loggreDic.TryAdd(cmdId, LogManager.GetLogger(string.Format("ChinaPnrApi.{0}", cmdId))); } return loggreDic[cmdId]; } } public class LockerDictionaryLogger : IGetLogger { Dictionary<string, Logger> loggreDic = new Dictionary<string, Logger>(); object locker = new object(); public Logger GetLogger(string cmdId) { if (!loggreDic.ContainsKey(cmdId)) { lock (locker) { if (!loggreDic.ContainsKey(cmdId)) { loggreDic.Add(cmdId, LogManager.GetLogger(string.Format("ChinaPnrApi.{0}", cmdId))); } } } return loggreDic[cmdId]; } }
測試代碼如下:
public static void Main() { IGetLogger conLogger = new ConcurrentDictionaryLogger(); IGetLogger lockerLogger = new LockerDictionaryLogger(); CodeTimer.Time("使用ConcurrentDictionary", 1000000, () => { ThreadPool.QueueUserWorkItem(o => { try { var logger = conLogger.GetLogger("AAA"); if (logger == null) { Console.WriteLine(string.Format("弟{0}個線程獲取到的值是 NULL", o)); } } catch (Exception ex) { Console.WriteLine(string.Format("弟{0}個線程出現問題, {1}", o, ex.Message)); } }); }); CodeTimer.Time("使用LockDictionary", 1000000, () => { ThreadPool.QueueUserWorkItem(o => { try { var logger = conLogger.GetLogger("AAA"); if (logger == null) { Console.WriteLine(string.Format("弟{0}個線程獲取到的值是 NULL", o)); } } catch (Exception ex) { Console.WriteLine(string.Format("弟{0}個線程出現問題, {1}", o, ex.Message)); } }); }); Console.WriteLine("已執行完成"); Console.ReadKey(); }
用Release模式編譯之后,測試的結果:
第一次:
第二次:
第三次:
從測試結果來看,都解決了我們上述的問題,總體的時間比值來看ConcurrentDictionary稍微優于LockDictionary, 但是差別不是很大, 第一次幾乎持平.
寫代碼還是要多注意線程安全的問題。
上面的CodeTimer用的是: http://www.49028c.com/JeffreyZhao/archive/2009/03/10/codetimer.html
新聞熱點
疑難解答