摘要

今天在园子里面有园友反馈关于[C#基础]说说lock到底锁谁?文章中lock(this)的问题。后来针对文章中的例子,仔细想了一下,确实不准确,才有了这篇文章的补充,已经对文章中的demo进行修改。

lock(this)

一个例子

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7.  
  8. namespace LockTest
  9. {
  10. class Program
  11. {
  12. static void Main(string[] args)
  13. {
  14. TestLock testlock = new TestLock();
  15. Thread th = new Thread(() =>
  16. {
  17. //模拟死锁:造成死锁,使lock无法释放,在i=5时,跳出死循环,释放lock
  18. testlock.DoWorkWithLock();
  19. });
  20. th.Start();
  21. Thread.Sleep();
  22. Thread th2 = new Thread(() =>
  23. {
  24. //这个地方你可能会有疑惑,但存在这种情况,比如你封装的dll,对其它开发人员不是可见的
  25. //开发人员很有可能在他的逻辑中,加上一个lock保证方法同时被一个线程调用,但这时有其它的线程正在调用该方法,
  26. //但并没有释放,死锁了,那么在这里就不会被执行,除非上面的线程释放了lock锁定的对象。这里的lock也可以理解为一个标识,线程1被锁定的对象
    //是否已经被释放,
    //如果没有释放,则无法继续访问lock块中的代码。
  27. lock (testlock)
  28. {
  29. // 如果该对象中lock(this)不释放(testlock与this指的是同一个对象),则其它线程如果调用该方法,则会出现直到lock(this)释放后才能继续调用。
  30. testlock.MotherCallYouDinner();
  31. testlock.DoWorkWithLock();
  32. }
  33. });
  34. th2.Start();
  35. Console.Read();
  36. }
  37. }
  38.  
  39. class TestLock
  40. {
  41. public static readonly object objLock = new object();
  42. /// <summary>
  43. /// 该方法,希望某人在工作的时候,其它人不要打扰(希望只有一个线程在执行)
  44. /// </summary>
  45. /// <param name="methodIndex"></param>
  46. public void DoWorkWithLock()
  47. {
  48. //锁当前对象
  49. lock (this)
  50. {
  51. Console.WriteLine("lock this");
  52. int i = ;
  53. while (true)
  54. {
  55. Console.WriteLine("At work, do not disturb...,Thread id is " + Thread.CurrentThread.ManagedThreadId.ToString());
  56. Thread.Sleep();
  57. if (i == )
  58. {
  59. break;
  60. }
  61. Console.WriteLine(i.ToString());
  62. i++;
  63. }
  64. }
  65. Console.WriteLine("lock dispose");
  66. }
  67. public void MotherCallYouDinner()
  68. {
  69. Console.WriteLine("Your mother call you to home for dinner.");
  70. }
  71. }
  72. }

测试

demo说明:main方法中,创建了一个对象testlock对象,线程1执行该对象的DoWorkWithLock方法,因为死锁(5s后释放),造成lock(this)无法释放,则导致了方法MotherCallYouDinner,DoWorkWithLock在线程2中无法被调用,直到lock(this)释放,lock(testlock)才能继续执行,可以这么理解,由于锁定的同一个对象,线程1释放了锁定的对象,其它线程才能访问。

lock(static readonly object)

那么通过lock(static object)方式呢,能不能保证lock块内的方法,同时只被一个线程执行呢,并且线程2能访问到MotherCallYouDinner方法。而不像上面出现的那种情况,如果不释放lock(this),导致线程2都无法执行代码逻辑。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7.  
  8. namespace LockTest
  9. {
  10. class Program
  11. {
  12. static void Main(string[] args)
  13. {
  14. TestLock testlock = new TestLock();
  15. Thread th = new Thread(() =>
  16. {
  17. //模拟死锁:造成死锁,使lock无法释放,在i=5时,跳出死循环,释放lock
  18. testlock.DoWorkWithLock();
  19. });
  20. th.Start();
  21. Thread.Sleep();
  22. Thread th2 = new Thread(() =>
  23. {
  24.  
  25. lock (testlock)
  26. {
  27. testlock.MotherCallYouDinner();
  28. testlock.DoWorkWithLock();
  29. }
  30. });
  31. th2.Start();
  32. Console.Read();
  33. }
  34. }
  35.  
  36. class TestLock
  37. {
  38. public static readonly object objLock = new object();
  39. /// <summary>
  40. /// 该方法,希望某人在工作的时候,其它人不要打扰(希望只有一个线程在执行)
  41. /// </summary>
  42. /// <param name="methodIndex"></param>
  43. public void DoWorkWithLock()
  44. {
  45. //锁
  46. lock (objLock)
  47. {
  48. Console.WriteLine("lock this");
  49. int i = ;
  50. while (true)
  51. {
  52. Console.WriteLine("At work, do not disturb...,Thread id is " + Thread.CurrentThread.ManagedThreadId.ToString());
  53. Thread.Sleep();
  54. if (i == )
  55. {
  56. break;
  57. }
  58. Console.WriteLine(i.ToString());
  59. i++;
  60. }
  61. }
  62. Console.WriteLine("lock dispose");
  63. }
  64. public void MotherCallYouDinner()
  65. {
  66. Console.WriteLine("Your mother call you to home for dinner.");
  67. }
  68. }
  69. }

测试

可以看到,将lock(this)更换为锁定私有的静态对象,线程2执行了,首先输出了“Your mother call you to home for dinner.”,同时实现了DoWorkWithLock方法中lock的代码块当前只被一个线程执行,直到lcok(objlock)被释放。因为锁定的对象,外部不能访问,线程2不再关心lock(this)是不是已经释放,都会执行,当然也保证了方法DoWorkWithLock同时被一个线程访问。

总结

1、避免使用lock(this),因为无法保证你提供的方法,在外部类中使用的时候,开发人员会不会锁定当前对象。

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

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

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

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

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

这里只是说明lock(this)的问题,虽然极端。但开发中,不能保证不会发生。

2、最好使用私有的静态只读的锁对象,保证不会影响其他逻辑的正常执行。

3、尽量避免死锁的发生。

资料,如果仍然不明白可以参考下面的链接

Why is lock(this) {…} bad?

非常感谢,@咕-咚 在上篇文章中,对demo提出的疑问,引发进一步的思考。当然,如果还有不妥的地方,欢迎指正,讨论。

[C#基础]说说lock到底锁谁?(补充与修改)的更多相关文章

  1. [C#基础]说说lock到底锁谁?

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  2. C# 说说lock到底锁谁?(1)

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  3. C# 说说lock到底锁谁?(2)

    摘要 今天在园子里面有园友反馈关于[C#基础]说说lock到底锁谁?文章中lock(this)的问题.后来针对文章中的例子,仔细想了一下,确实不准确,才有了这篇文章的补充,已经对文章中的demo进行修 ...

  4. 说说lock到底锁谁(II)?

    摘要 今天在园子里面有园友反馈关于[C#基础]说说lock到底锁谁?文章中lock(this)的问题.后来针对文章中的例子,仔细想了一下,确实不准确,才有了这篇文章的补充,已经对文章中的demo进行修 ...

  5. 说说lock到底锁谁(I)?

    写在前面 最近一个月一直在弄文件传输组件,其中用到多线程的技术,但有的地方确实需要只能有一个线程来操作,如何才能保证只有一个线程呢?首先想到的就是锁的概念,最近在我们项目组中听的最多的也是锁谁,如何锁 ...

  6. 说说lock到底要锁谁?

    波安搬... http://www.cnblogs.com/wolf-sun/p/4209521.html ---------------------------------------------- ...

  7. Go基础系列:互斥锁Mutex和读写锁RWMutex用法详述

    sync.Mutex Go中使用sync.Mutex类型实现mutex(排他锁.互斥锁).在源代码的sync/mutex.go文件中,有如下定义: // A Mutex is a mutual exc ...

  8. synchronized到底锁住的是谁?

    本文代码仓库:https://github.com/yu-linfeng/BlogRepositories/tree/master/repositories/sync 先来一道校招级并发编程笔试题 题 ...

  9. oracle(enquences & latches )lock (oracle 锁大全)

    资料来自官方文档: https://docs.oracle.com/database/121/CNCPT/consist.htm#CNCPT1333 https://docs.oracle.com/d ...

随机推荐

  1. magento开发手册之目录结构

    magento是一个很优秀的电商系统,很多朋友会用它部署自己的电商网站,少不了二次开发.下面我们随着ytkah来一起认识一下magento开发手册之目录结构吧. /app – 程序根目录 /app/e ...

  2. 初试GH-OST(转)

    最近老板让做一个gh-ost和pt-osc 的对比测试,本文将对两者做对比. 一.原理和所用说明   PT-OSC GH-OST 原理 1.创建一个和要执行 alter 操作的表一样的新的空表结构(是 ...

  3. java poi生成excel(个人例子js-jsp-java)

    js代码: function exportRepQudl(){ if(confirm("导出输出页面内容?")){ var id = $("input[name='id' ...

  4. 158A

    #include <iostream> #include <algorithm> using namespace std; int main() { int groups[10 ...

  5. (转)Marathon健康检查

    健康检查是需要每个应用运行监控检查任务的. 1.默认的健康检查是延迟才能让mesos知道任务的状态是否健康. 2.marathon提供一个任务资源的健康成员访问的REST API接口. 如果HTTP的 ...

  6. linux iscsi挂载与卸载

    iscsiadm -m discovery -t sendtargets -p 192.168.4.245:3260 #扫描ISCSI Target 列出所有LUN iscsiadm -m node ...

  7. Ireport第一张web项目报表。

    原先项目里面的统计分析报表都是和普通的系统页面一样开发的,SSM架构,从数据库一层一层往前面传数据,最后通过jsp表现出来,这次在领导的建议下使用IReport进行报表开发,果然还是要使用工具啊,社会 ...

  8. python爬虫-基础入门-爬取整个网站《2》

    python爬虫-基础入门-爬取整个网站<2> 描述: 开场白已在<python爬虫-基础入门-爬取整个网站<1>>中描述过了,这里不在描述,只附上 python3 ...

  9. JavaScript 字符串replace全局替换

    一般使用replace let str = "2018-8-14"; str.replace('-','/')//2018/8-14 并没有替换第二个”-“, 所以我们用正则表达式 ...

  10. NHibernate之旅系列文章导航

    NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实 ...