在开发多线程的应用程序时,我们会大量用到 lock (...) {} 块。如果 lock 的对象比较多,非常容易发生死锁。死锁的发生很难预料,而且一旦发生在界面线程上,界面就不再刷新响和应用户输入;如果发生在后台线程,后台线程也就阻塞不工作了,死锁必然会导致应用程序不可用。在.NET里发生死锁的原因是什么?

以 C# 为例,通常 lock 语句是被转化为对一个资源的无限长时间的等待,所以一旦资源被占用而又永不释放,那么必然死锁。

那么如何规避的危害呢?应用程序应该避免 lock(obj) 块,推荐使用 Monitor.TryEnter(obj, millisecondsTimeout) 代替,二者的第一个参数意义相同,而后者还可以设置等待超时时间,一旦在限定的时间内无法获得锁,那么 TryEnter 就会返回  false。这样就不会造成死锁,无法获得资源,业务程序可以采取重试或抛异常的方式进行善后处理。

Monitor.TryEnter 和 Monitor.Exit 必须成对出现,为了简化代码,可以用一个实现IDisposeable的类来封装这个过程:

/// <summary>
    /// 会自动释放的锁,可设置等待超时
    /// </summary>
    public class Lock : IDisposable
    {
        /// <summary>
        /// 默认超时设置
        /// </summary>
        public static int DefaultMillisecondsTimeout = 15000; // 15S
        private object _obj;

/// <summary>
        /// 构造 
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        public Lock(object obj)
        {
            TryGet(obj, DefaultMillisecondsTimeout, true);
        }

/// <summary>
        /// 构造
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        /// <param name="millisecondsTimeout">超时设置</param>
        public Lock(object obj, int millisecondsTimeout)
        {
            TryGet(obj, millisecondsTimeout, true);
        }

/// <summary>
        /// 构造
        /// </summary>
        /// <param name="obj">想要锁住的对象</param>
        /// <param name="millisecondsTimeout">超时设置</param>
        /// <param name="throwTimeoutException">是否抛出超时异常</param>
        public Lock(object obj, int millisecondsTimeout, bool throwTimeoutException)
        {
            TryGet(obj, millisecondsTimeout, throwTimeoutException);
        }

private void TryGet(object obj, int millisecondsTimeout, bool throwTimeoutException)
        {
            if (Monitor.TryEnter(obj, millisecondsTimeout))
            {
                _obj = obj;
            }
            else
            {
                if (throwTimeoutException)
                {
                    throw new TimeoutException();
                }
            }
        }

/// <summary>
        /// 销毁,并释放锁
        /// </summary>
        public void Dispose()
        {
            if (_obj != null)
            {
                Monitor.Exit(_obj);
            }
        }

/// <summary>
        /// 获取在获取锁时是否发生等待超时
        /// </summary>
        public bool IsTimeout
        {
            get
            {
                return _obj == null;
            }
        }
    }

调用例子:

using (Lock l = new Lock(obj, 10000))
{
    .... 
}

这样在代码离开 using 块后,会自动执行 Monitor.Exit释放锁。

是不是很棒? :)

最新文章

  1. [LeetCode] Encode String with Shortest Length 最短长度编码字符串
  2. Particle System(粒子系统)
  3. 在csdn里markdown感受
  4. PHP判断用户所在国家并跳转对应的目录
  5. http协议本身能获取客户端Mac地址问题
  6. YII开发技巧分享——模型(models)中rules自定义验证规则
  7. return、break、continue的区别
  8. C#压缩文件为zip格式
  9. 8.2.1.2 How MySQL Optimizes WHERE Clauses MySQL 优化WHERE 子句
  10. hdu2309ICPC Score Totalizer Software
  11. 绳关节(b2RopeJoint)
  12. Day03——类、值和对象
  13. session的一些方法
  14. js对象中动态读取属性值 动态属性值 js正则表达式全局替换
  15. 201521123015 《Java程序设计》第5周学习总结
  16. 利用Windows性能计数器(PerformanceCounter)监控
  17. 探索C++多态和实现机理
  18. EBS R12安装升级(FRESH)(二)
  19. DocKer 创建容器 镜像端口映射失败
  20. 解决ui-router路由监听$stateChangeStart、$stateChangeSuccess、$stateChangeError不执行的问题

热门文章

  1. Jmeter联机负载时报错: connection refused to host localhost,nested exception is:java.net ConnectException:Connection refused:connect
  2. Elasticsearch技术解析与实战--shard&amp;replica机制
  3. 数据结构之AVL
  4. dbgrideh 中的keylist,picklist的用法
  5. jquery中对地址中的中文进行encodeURI编码
  6. 自动化运维:(2)Shell 编程的流程控制
  7. HADOOP依赖
  8. 多边形求重心 HDU1115
  9. 二项式定理+前缀Sigma
  10. Tornado WebSocket简单聊天