代码是在多线程环境下,做了简单的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的地方做了判断的处理,但是在多线程的时候还是会出现在取的时候取不到的问题。可以参考下面截图 :
从错误异常很容易判断,是在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.VEVb.com/JeffreyZhao/archive/2009/03/10/codetimer.html
新闻热点
疑难解答