在开发多线程的应用程序时,我们会大量用到 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释放锁。

是不是很棒? :)

利用 Monitor.TryEnter 来规避 .NET 线程死锁的源代码的更多相关文章

  1. 《C#多线程编程实战》1.11 Monitor.TryEnter()避免死锁

    这章的内容是真的有意思 特别是代码. 先贴上代码: class Program { static void Main(string[] args) { object lock1 = new objec ...

  2. 线程系列08,实现线程锁的各种方式,使用lock,Montor,Mutex,Semaphore以及线程死锁

    当涉及到多线程共享数据,需要数据同步的时候,就可以考虑使用线程锁了.本篇体验线程锁的各种用法以及线程死锁.主要包括: ※ 使用lock处理数据同步※ 使用Monitor.Enter和Monitor.E ...

  3. .net学习之多线程、线程死锁、线程通信 生产者消费者模式、委托的简单使用、GDI(图形设计接口)常用的方法

    1.多线程简单使用(1)进程是不执行代码的,执行代码的是线程,一个进程默认有一个线程(2)线程默认情况下都是前台线程,要所有的前台线程退出以后程序才会退出,进程里默认的线程我们叫做主线程或者叫做UI线 ...

  4. 对象及变量的并发访问(同步方法、同步代码块、对class进行加锁、线程死锁)&内部类的基本用法

    主要学习多线程的并发访问,也就是使得线程安全. 同步的单词为synchronized,异步的单词为asynchronized 同步主要就是通过锁的方式实现,一种就是隐式锁,另一种是显示锁Lock,本节 ...

  5. Python并发编程-进程 线程 同步锁 线程死锁和递归锁

    进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...

  6. Atitit.线程 死锁 跑飞 的检测与自动解除 与手动解除死锁 java c# .net php javascript.

    Atitit.线程 死锁 跑飞 的检测与自动解除 与手动解除死锁 java c# .net php javascript. 1. 现象::主程序卡住无反应,多行任务不往下执行 1 2. 原因::使用j ...

  7. Python3 进程 线程 同步锁 线程死锁和递归锁

    进程是最小的资源单位,线程是最小的执行单位 一.进程 进程:就是一个程序在一个数据集上的一次动态执行过程. 进程由三部分组成: 1.程序:我们编写的程序用来描述进程要完成哪些功能以及如何完成 2.数据 ...

  8. java 线程死锁的检测

    java 线程死锁的检测   例子程序: import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executo ...

  9. troubleshoot之:用control+break解决线程死锁问题

    目录 简介 死锁的代码 control+break命令 Full thread dump 死锁检测 Heap信息 总结 简介 如果我们在程序中遇到线程死锁的时候,该怎么去解决呢? 本文将会从一个实际的 ...

随机推荐

  1. Selenium 2自动化测试实战37(自动发邮件功能)

    自动发邮件功能 例如,如果想在自动化脚本运行完成之后,邮箱就可以收到最新的测试报告结果.SMTP(Simple Mail Transfer Protocol)是简单邮件传输协议,它是一组用于由源地址到 ...

  2. 常见 Web 性能优化方式

    这篇文章是我阅读 Web Performance 101 之后的进行的粗糙的翻译作为笔记,英语还行的童鞋可以直接看原文. 这篇文章主要介绍了现代 web 加载性能(注意不涉及代码算法等),学习为什么加 ...

  3. Cloudera Manager 常见异常

    1. Cloudera Management Service服务全部无法启动 观察到的现象: (1)cm service 组件无法启动,启动时服务请求超时终止:(2)主机信息也无法获取到,一直提示“无 ...

  4. (转载)悟透JavaScript

    引子 编程世界里只存在两种基本元素,一个是数据,一个是代码.编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力. 数据天生就是文静的,总想保持自己固有的本色:而代码却天生活泼,总想改变这个 ...

  5. rsync重启的shell脚本

    https://zhidao.baidu.com/question/2078634436717887028.html

  6. 自动化运维:(1)认识 Shell

    目录 (一)运维是什么? (二)什么是 Shell? (三)Shell的分类 (四)Shell脚本 (五)Shell的变量 (六)表达式 (七)Linux常见符号 (八)常见命令 (一)自动化运维是什 ...

  7. 架构模式: 健康检查API

    架构模式: 健康检查API 上下文 您已应用微服务架构模式.有时,服务实例可能无法处理仍在运行的请求.例如,它可能已用完数据库连接.发生这种情况时,监控系统应生成警报.此外,负载平衡器或服务注册表不应 ...

  8. 今天发现一个Window系统服务增删改查神器:NSSM

    官网地址:https://nssm.cc Win10系统下这个:https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip 官方的帮助,英语的,可以大概看一下: htt ...

  9. 请求头出现Provisional headers are shown

    provisional headers are shown 知多少:https://juejin.im/post/5c00980751882518805add83 请求头出现Provisional h ...

  10. python下对目录&文件的操作

    Windows桌面上: 总文件夹 子文件夹01 文档01.txt-------------------------------------------------------------------- ...