本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
Monitor的扩展支持string的超时锁
talkchan · 494浏览 · 发布于2020-11-23 +关注

对Monitor的使用可以防止lock的时间过长并且可以设置其对应的超时时间达到对预期代码的一个控制,合理的使用timeout可以有助于程序的健壮性。但是对于不同的并发程序可能某些时候我们需要的粒度是不一样的,从而产生的一个问题是需要更细粒度的锁来保证,又因为默认的字符串无法共享导致的无法通过string来进行锁粒度的细分所以可能需要自己重写一个锁来保证到达更细粒度的控制,可能这时候有人要说不是有string.Intern可以进行字符串保留,达到类似的效果,对于这个我只想说内部确定的字符串确实可以达到这个效果,但是因为字符串保留机制就导致了gc不会对此进行回收,会导致如果外部输入的string是不可控的情况下就可以给程序造成诸如oom的问题,对此我抛砖引玉希望各位博友可以提出宝贵的意见或者建议,或者有现成的更好的解决方案也请分享一下,下面贴代码:

public class MonitorStr
    {
        private MonitorStr() { }
        private static ConcurrentDictionary _lockDics = new ConcurrentDictionary();
        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保证并发多线程安全且高效。

如何使用

var key = "testKey";
            var timeoutMillis = 3000;
            var acquired = false;
            try
            {

                 acquired = MonitorStr.TryEnter(key, timeoutMillis);
                if (acquired)
                {
                    //Do Something
                }
                else
                {
                    throw new XXXTimeOutException();
                }
            }
            finally
            {
                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
                {
                    MonitorStr.Exit(key);
                }
            }


相关推荐

PHP实现部分字符隐藏

沙雕mars · 1312浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 896浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 957浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 953浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1076浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
大家好,我是一名专注技术开发的技术屌丝,有什么问题可以互相交流,一起学习进步,谢谢。
分类专栏
小鸟云服务器
扫码进入手机网页