这篇文章主要介绍了C#中lock死锁的用法,对于共享资源的访问及C#程序设计的安全性而言,有着非常重要的意义!需要的朋友可以参考下

链接:http://www.jb51.net/article/54309.htm

在c#中有个关键字lock,它的作用是锁定某一代码块,让同一时间只有一个线程访问该代码块,本文就来谈谈lock关键字的原理和其中应注意的几个问题:

lock的使用原型是:

1
2
3
4
lock(X)
{
  //需要锁定的代码....
}

首先要明白为什么上面这段话能够锁定代码,其中的奥妙就是X这个对象,事实上X是任意一种引用类型,它在这儿起的作用就是任何线程执行到lock(X)时候,X需要独享才能运行下面的代码,若假定现在有3个线程A,B,C都执行到了lock(X)而ABC因为此时都占有X,这时ABC就要停下来排个队,一个一个使用X,从而起到在下面的代码块内只有一个线程在运行(因为此时只有一个线程独享X,其余两个在排队),所以这个X必须是所有要执行临界区域代码进程必须共有的一个资源,从而起到抑制线程的作用。

下面再来谈谈lock使用中会遇到和注意的问题,lock最需要注意的一个问题就是线程死锁!

在MSDN上列出了3个典型问题:

通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:

如果实例可以被公共访问,将出现 lock (this) 问题。

如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。

由于进程中使用同一字符串的任何其他代码将共享同一个锁,所以出现 lock(“myLock”) 问题。

最佳做法是定义 private 对象来锁定, 或 private shared 对象变量来保护所有实例所共有的数据。

(1)lock (this) 问题:

假定有两个类:

1
2
class A{}
class B{}

有两个公共对象:

1
2
A a=new A();
B b=new B();

首先在A中若有一函数内的代码需要锁定:

代码1:

1
2
3
4
5
6
7
8
lock(this)//this在这里就是a
{
 //....
 lock(b)
 {
//......
 }
}

然而此时B中某函数也有如下代码需要锁定:

代码2:

1
2
3
4
5
6
7
8
lock(this)//this在这里就是b
{
 //....
 lock(a)
 {
//......
 }
}

设想一下上面两段代码在两个线程下同时执行会有什么后果?

结果就是,代码1执行到lock(this)后a被锁定,代码2执行到lock(this)后b被锁定,然后代码1需求b,代码2需求a,此时两个需求都被相互占有出现僵持状态,程序死锁了。

(2)lock(typeof (MyType))问题:

假定有两个公共变量:

1
int a;float b;

下面看如下代码

代码3:

1
2
3
4
5
6
7
8
lock(typeof(a))//typeof(a)就是System.type.Int类型
{
 //....
 lock(typeof(b))
 {
//......
 }
}

又有如下代码:

代码4:

1
2
3
4
5
6
7
8
lock(typeof(b))//typeof(b)就是System.type.Float类型
{
 //....
 lock(typeof(a))
 {
//......
 }
}

若有两个进程分别同时进入上面两个代码外层的lock,就分别锁定了System.type.Int和System.type.Float,而马上它们又需求System.type.Float和System.type.Int,彼此相互占有,彼此僵持,程序进入死锁状态!

(3)字符串问题 :

在阐述这个问题之前,有一个知识大家必须知道:C#中字符串被公共语言运行库 (CLR)“暂留”。这意味着整个程序中任何给定字符串都只有一个实例,就是这同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。

言下之意就是假定有两个类分别有两个字符串:

1
2
3
4
5
6
7
8
9
10
11
class A
{
 string a="abc";
 string b="def";
}
 
class c
{
 string c="abc";
 string d="def";
}

事实上a和c引用的是同一个字符串"abc",b和d引用的是同一个字符串"def"

现在如果在两个类中有如下代码

在类A中有代码5:

1
2
3
4
5
6
7
8
lock(b)//b是"def"
{
 //....
 lock(a)//a是"abc"
 {
//......
 }
}

在类B中有代码6:

1
2
3
4
5
6
7
8
lock(c)//c是"abc"
{
 //....
 lock(d)//d是"def"
 {
//......
 }
}

那么代码5和代码6同时有两个线程执行结果可想而知:在两个线程执行到外层lock代码时"def"和"abc"被锁定。接着他们在内部lock处同时需求"abc"和"def",而此时两个字符串被两个进程彼此占有,程序又死锁了!所以MSDN说:锁定字符串尤其危险!最好不要使用!

MSDN最后说了:最佳做法是定义 private 对象来锁定, 或 private shared 对象变量来保护所有实例所共有的数据。 
在个人看来,也不是绝对安全,这里就举出一个例子:

假定有一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class A
{
 private Object a=new Object();
 private Object b=new Object();
 public void x()
 {
  lock(a)
  {
    //.....
    lock(b)
    {
     //....
    }
  }
 }
 public void y()
 {
  lock(b)
  {
    //.....
    lock(a)
    {
     //....
    }
  }
 }
}

现在假定有两个线程同时执行函数x()和y();结果private对象a和b分别在外层lock锁定,接着两个线程在内部又立马需求b和a,a,b彼此占有又彼此需求,程序死锁。

所以具体要看情况而定,但是定义 private 对象来锁定至少可以降低风险。

希望本文所述C#中lock的应用对大家的C#程序设计有所帮助。

C#中lock死锁实例教程的更多相关文章

  1. 详解Linux交互式shell脚本中创建对话框实例教程_linux服务器

    本教程我们通过实现来讲讲Linux交互式shell脚本中创建各种各样对话框,对话框在Linux中可以友好的提示操作者,感兴趣的朋友可以参考学习一下. 当你在终端环境下安装新的软件时,你可以经常看到信息 ...

  2. [Python][flask][flask-wtf]关于flask-wtf中API使用实例教程

    简介:简单的集成flask,WTForms,包括跨站请求伪造(CSRF),文件上传和验证码. 一.安装(Install) 此文仍然是Windows操作系统下的教程,但是和linux操作系统下的运行环境 ...

  3. c#初学-多线程中lock用法的经典实例

    本文转载自:http://www.cnblogs.com/promise-7/articles/2354077.html 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被 ...

  4. 多线程中lock用法的经典实例

    多线程中lock用法的经典实例 一.Lock定义     lock 关键字可以用来确保代码块完成运行,而不会被其他线程中断.它可以把一段代码定义为互斥段(critical section),互斥段在一 ...

  5. Web 开发中应用 HTML5 技术的10个实例教程

    HTML5 作为下一代网站开发技术,无论你是一个 Web 开发人员或者想探索新的平台的游戏开发者,都值得去研究.借助尖端功能,技术和 API,HTML5 允许你创建响应性.创新性.互动性以及令人惊叹的 ...

  6. Android中ViewPager实现滑动条及与Fragment结合的实例教程

    ViewPager类主要被用来实现可滑动的视图功能,这里我们就来共同学习Android中ViewPager实现滑动条及与Fragment结合的实例教程,需要的朋友可以参考下 自主实现滑动指示条先上一个 ...

  7. 一个线程中lock用法的经典实例

    /* 该实例是一个线程中lock用法的经典实例,使得到的balance不会为负数 同时初始化十个线程,启动十个,但由于加锁,能够启动调用WithDraw方法的可能只能是其中几个 作者:http://h ...

  8. 深入浅出SQL Server中的死锁

    简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...

  9. 14.5.5.1 An InnoDB Deadlock Example 一个InnoDB 死锁实例

    14.5.5.1 An InnoDB Deadlock Example 一个InnoDB 死锁实例 下面的例子演示了一个错误可以发生当一个lock 请求会导致一个死锁,例子设计2个客户端,A和B: J ...

随机推荐

  1. GCC编译器原理(一)03------GCC 工具:gprof、ld、libbfd、libiberty 和libopcodes

    1.3.7 gprof:性能分析工具 参考文档:https://www.cnblogs.com/andashu/p/6378000.html gprof是GNU profile工具,可以运行于linu ...

  2. mongodb 添加字段并设置默认值

    db.doc名称.update({}, {$set: {新字段名称: 默认值}}, false, true) 如:db.fly_bill.update({}, {$set: {usableStatus ...

  3. springboot11-01-security入门

    场景: 有3个页面:首页.登录页.登录成功后的主页面,如下图: 如果没有登录,点击“去主页”,会跳转到登录页 如果已经登录,点击“去主页”,跳转到主页,显示“hello 用户名” 下面用springb ...

  4. keras中的重要函数

    https://blog.csdn.net/u012969412/article/details/70882296

  5. 配置JDK和Tomcat环境变量

    配置JDK和Tomcat环境变量 一.安装JDK和Tomcat 安装JDK:直接运行jdk-7-windows-i586.exe可执行程序,默认安装即可. 备注:路径可以其他盘符,不建议路径包含中文名 ...

  6. Shiro入门 - 通过自定义Realm连数数据库进行认证(md5+salt形式)

    shiro-realm-md5.ini [main] #定义凭证匹配器 credentialsMatcher=org.apache.shiro.authc.credential.HashedCrede ...

  7. 工具方法 .js

    1. 获取url问号后面,name的值 /** * *?id=123&a=b * @return object */export function urlParse(){ let url = ...

  8. Pycharm 2018 Activation code 在线激活

    1. 下载官方 pycharm https://www.jetbrains.com/pycharm/download/ 2. 点击获取激活码 点击获取激活码 2.1 打开  hosts 文件 2.2  ...

  9. PHP反序列化漏洞学习

    serialize:序列化 unserialize: 反序列化 简单解释: serialize 把一个对象转成字符串形式, 可以用于保存 unserialize 把serialize序列化后的字符串变 ...

  10. requests支持socks5代理了

    记录下 以前: import socket import socks from requests_html import HTMLSession session=HTMLSession() socks ...