对Monitor的使用能够防止lock的时间过长而且能够设置其对应的超时时间达到对预期代码的一个控制,合理的使用timeout能够有助于程序的健壮性。可是对于不一样的并发程序可能某些时候咱们须要的粒度是不同的,从而产生的一个问题是须要更细粒度的锁来保证,又由于默认的字符串没法共享致使的没法经过string来进行锁粒度的细分因此可能须要本身重写一个锁来保证到达更细粒度的控制,可能这时候有人要说不是有string.Intern能够进行字符串保留,达到相似的效果,对于这个我只想说内部肯定的字符串确实能够达到这个效果,可是由于字符串保留机制就致使了gc不会对此进行回收,会致使若是外部输入的string是不可控的状况下就能够给程序形成诸如oom的问题,对此我抛砖引玉但愿各位博友能够提出宝贵的意见或者建议,或者有现成的更好的解决方案也请分享一下,github地址下面贴代码:git
public class MonitorStr { private MonitorStr() { } private static ConcurrentDictionary<string, MonitorStrEntry> _lockDics = new ConcurrentDictionary<string, MonitorStrEntry>(); private const int _concurrentCount = 31; private static object[] _lockers = new object[_concurrentCount]; static MonitorStr() { for (int i = 0; i < _concurrentCount; i++) { _lockers[i] = new object(); } } private static int GetIndex(string key) { return Math.Abs(key.GetHashCode() % _concurrentCount); } public static bool TryEnter(string key, int timeoutMillis) { if (string.IsNullOrWhiteSpace(key)) throw new ArgumentNullException(nameof(key)); MonitorStrEntry entry = null; var locker = _lockers[GetIndex(key)]; lock (locker) { if (!_lockDics.TryGetValue(key, out entry)) { entry = new MonitorStrEntry(); _lockDics[key] = entry; } entry.Increment(); } var acquired = Monitor.TryEnter(entry, timeoutMillis); if (!acquired) entry.Decrement(); return acquired; } public static void Exit(string key) { var entry = _lockDics[key]; Monitor.Exit(entry); if (entry.Decrement() == 0) { var locker = _lockers[GetIndex(key)]; lock (locker) { if (entry.CanRemove()) { Console.WriteLine(key + "remove"); _lockDics.TryRemove(key, out var v); } } } } class MonitorStrEntry { private int _lockCount; public int Increment() { Interlocked.Increment(ref _lockCount); return _lockCount; } public int Decrement() { Interlocked.Decrement(ref _lockCount); return _lockCount; } public bool CanRemove() { return _lockCount == 0; } } }
这个代码的原理就是利用数组对象锁和传入的string key进行对不一样key之间的粒度的区分,由于不一样的key之间的hashcode不一致因此对取到的锁对象locker也不同,达到下降锁并发的级别,字典存储的entry内部维护一个锁的加锁次数达,利用cas保证并发多线程安全且高效。github
如何使用数据库
var key = "testKey"; var timeoutMillis = 3000; var acquired = false; try { acquired = MonitorStr.TryEnter(key, timeoutMillis); if (acquired) { //Do Something } else { throw new XXXTimeOutException(); } } finally { if(acquired) MonitorStr.Exit(key); }
哪一个场景下可使用呢?数组
//使用场景,诸如多线程查数据库环境的状况下 var userId = "xxxx"; var user = Cache.Query(userId); if (user == null) { var acquired = false; try { acquired = MonitorStr.TryEnter(key, timeoutMillis); if (acquired) { //Do Something user = Cache.Query(userId); if (user == null) { user = DB.Query(userId); } } else { throw new XXXTimeOutException(); } } finally { if(acquired) MonitorStr.Exit(key); } }
谢谢安全