博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 线程手册 第三章 使用线程 小心死锁
阅读量:7086 次
发布时间:2019-06-28

本文共 3324 字,大约阅读时间需要 11 分钟。

尽管使用线程同步对线程安全来说是必须的,但是如果没有用好的话就可能导致死锁。因此,理解什么是死锁并知道如何避免死锁是非常重要的。当两个或两个以上的线程等待两个或多于两个锁被释放然后程序中的逻辑导致锁永远都不会被释放时死锁就发生了。图3描述了一个典型的死锁场景。

图3

 

在上图中,线程1获得通过进入一个对象的关键区域获得这个对象的锁L1。在关键部分中线程1想要获取锁L2。线程2获得锁L2同时还想获得锁L1。所以,现在线程1无法获得锁L2而线程2无法获得锁L1,因为这两个线程彼此拥有对方需要的锁而又不会释放它们。结果是两个线程都进入无限等待或者死锁。

阻止潜在的死锁发生的最好的方式是避免在同一时间获取多个锁,这种情况不是经常发生。然而,如果必须这样做的话,你需要一种策略来保证你可以在一个稳定的、定义好的顺序获得多个锁。这取决于每个程序是如何设计的,不同的同步策略在避免死锁时可能很不同。没有一种方法可以用来避免所有类型的死锁。大多数时候,死锁不会被检测到除非程序被部署到一个大规模运行环境中。如果我们能够在我们程序的测试阶段就能发现死锁的话那么我们可以说是非常幸运的。当然这取于很多因素,开发人员水平,测试人员水平,开发/测试工具,经验等等。

一个很关键的但是经常被忽略的关于锁定策略部分的是文档。不幸的是,即使花费很多时间来设计避免死锁的同步策略,在记录这个过程的文档方面也仅仅做了很少的工作。至少,每个方法都应该有一个关于它是如何确定它要获得的锁以及描述方法内的关键部分的文档。

让我们来看一个例子,Deadlock.cs:

/*************************************/* copyright (c) 2012 daniel dong *  * author:daniel dong * blog:  www.cnblogs.com/danielwise * email: guofoo@163.com *  */using System;using System.Collections.Generic;using System.Text;using System.Threading;namespace Deadlock{    class DL    {        int field1 = 0;        int field2 = 0;        private object lock1 = new int[1];        private object lock2 = new int[1];        public void First(int val)        {            lock (lock1)            {                Console.WriteLine("First: Acquired lock 1: "                    + Thread.CurrentThread.GetHashCode() + " Now Sleeping.");                //Try commenting Thread.Sleep()                Thread.Sleep(1000);                Console.WriteLine("First: Acquired lock 1: "                    + Thread.CurrentThread.GetHashCode() + " Now wants lock2.");                lock (lock2)                {                    Console.WriteLine("First: Acquired lock 2: "                        + Thread.CurrentThread.GetHashCode());                    field1 = val;                    field2 = val;                }            }        }        public void Second(int val)        {            lock (lock2)            {                Console.WriteLine("Second: Acquired lock 2: "                    + Thread.CurrentThread.GetHashCode());                lock (lock1)                {                    Console.WriteLine("Second: Acquired lock 1: "                        + Thread.CurrentThread.GetHashCode());                    field1 = val;                    field2 = val;                }            }        }    }    public class MainApp    {        DL d = new DL();        public static void Main()        {            MainApp m = new MainApp();            Thread t1 = new Thread(new ThreadStart(m.Run1));            t1.Start();            Thread t2 = new Thread(new ThreadStart(m.Run2));            t2.Start();            Console.ReadLine();        }        public void Run1()        {            this.d.First(10);        }        public void Run2()        {            this.d.Second(10);        }    }}

Deadlock.cs 的输出结果如下:

在Deadlock.cs 中,线程t1调用First()方法获得lock1, 然后睡眠1秒钟。同时线程t2调用Second()方法并获得lock2. 然后在尝试在同一方法内获得lock1.但是线程1已经拥有lock1, 所以线程t2不得不等待线程1释放lock1. 当线程t1醒来后,它尝试获得lock2。而lock2已经被线程t2获得所以线程t1只能等待线程t2释放它。结果是发生了死锁而程序卡死。把方法First()中的Thread.Sleep()方法注释掉不会导致死锁,至少是个临时解决方案。因为线程t1会在线程t2之前获得lock2. 但是在真实场景中,Thread.Sleep()时发生的可能是一个连接数据库操作,导致线程t2在线程t1之前获得lock2, 最终结果也是死锁。这个例子告诉我们在任何多线程应用程序中设计出一个好的锁定方案是多么重要!一个好的锁定方案可能通过让所有线程运行在一个定义好的行为中以合作方式获得锁。在上面的例子中,线程t1不应该请求锁除非它被线程t2释放, 反之亦然。这些决定取决于特定应用场景而不具有一般性。测试各种锁定方案是同等重要的,因为死锁通常发生在那些缺少压力和功能性测试的系统中。

 

下一篇介绍两个关于线程安全的例子…

你可能感兴趣的文章
负载均衡集群中如何隐藏VIP
查看>>
CUDA编程接口:使用nvcc编译器的兼容性
查看>>
IOS学习——UI基础UIWindow、UIView(五)
查看>>
Silverlight MMORPG《窝窝世界》游戏视频
查看>>
Oracle VM VirtualBox上安装windows server2008R2做SharePointServer2010开发(下
查看>>
数据收集利器 cAdvisor - 每天5分钟玩转 Docker 容器技术(82)
查看>>
<rhel6+pptpd+freeradius+mysql>
查看>>
前端有哪些优质资源可以利用?
查看>>
[ASP.NET]跨页面传值
查看>>
名词:topology、architecture和struct,究竟什么才是架构?
查看>>
极速理解设计模式系列:20.模板方法模式(Template Method Pattern)
查看>>
如何使用mysql(lamp)分离环境搭建dedecms织梦网站及apache服务器常见的403http状态码及其解决方法...
查看>>
CentOS6.5 keepalived详解及实现Nginx服务的高可用性
查看>>
OSPF路由过滤测试
查看>>
Linux基础命令小结(上)-Linux学习日记
查看>>
SMS 2003 系列 —OSD部署指南
查看>>
乾颐堂HCIE1 OSPF基础和Hello报文以及邻居的基本排错
查看>>
对VS2008生成智能win32程序简单理解
查看>>
Oracle DG 最大保护(Maximize Protection)和最高可用性(Maximize Availability)异同
查看>>
java中的类修饰符、成员变量修饰符、方法修饰符。
查看>>