利用 Monitor.TryEnter 来规避 .NET 线程死锁的源代码
在开发多线程的应用程序时,我们会大量用到 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释放锁。
是不是很棒? :)
最新文章
- [LeetCode] Encode String with Shortest Length 最短长度编码字符串
- Particle System(粒子系统)
- 在csdn里markdown感受
- PHP判断用户所在国家并跳转对应的目录
- http协议本身能获取客户端Mac地址问题
- YII开发技巧分享——模型(models)中rules自定义验证规则
- return、break、continue的区别
- C#压缩文件为zip格式
- 8.2.1.2 How MySQL Optimizes WHERE Clauses MySQL 优化WHERE 子句
- hdu2309ICPC Score Totalizer Software
- 绳关节(b2RopeJoint)
- Day03——类、值和对象
- session的一些方法
- js对象中动态读取属性值 动态属性值 js正则表达式全局替换
- 201521123015 《Java程序设计》第5周学习总结
- 利用Windows性能计数器(PerformanceCounter)监控
- 探索C++多态和实现机理
- EBS R12安装升级(FRESH)(二)
- DocKer 创建容器 镜像端口映射失败
- 解决ui-router路由监听$stateChangeStart、$stateChangeSuccess、$stateChangeError不执行的问题
热门文章
- Jmeter联机负载时报错: connection refused to host localhost,nested exception is:java.net ConnectException:Connection refused:connect
- Elasticsearch技术解析与实战--shard&;replica机制
- 数据结构之AVL
- dbgrideh 中的keylist,picklist的用法
- jquery中对地址中的中文进行encodeURI编码
- 自动化运维:(2)Shell 编程的流程控制
- HADOOP依赖
- 多边形求重心 HDU1115
- 二项式定理+前缀Sigma
- Tornado WebSocket简单聊天