请选择 进入手机版 | 继续访问电脑版

马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#技术求助 ASP.NET技术求助

【源码下载】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接外包项目】 面试-葵花宝典下载

官方一群:

官方二群:

c# lock 锁

[复制链接]
查看2352 | 回复2 | 2019-8-15 18:14:51 | 显示全部楼层 |阅读模式
  • lock语句

lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lock。

  • 为什么必要锁

作为C#的程序员来说,在遇到线程同步的需求时最常用的就是lock关键字。lock 的目的很明确,就是不想让别人使用这段代码,体如今多线程情况下,只允许当前线程执行该代码区域,其他线程等待直到该线程执行结束;这样可以多线程避免同时使用某一方法造成数据杂乱。

  • lock的等效代码

在.NET的多线程程序中,经常会遇到lock关键字来控制同步,比如下列代码:

private object o = new object();

public void Work()

{

  lock(o)

  {

    //做一些必要线程同步的工作

  }

}

事实上,lock这个关键字是C#为方便程序员而定义的语法,它等效于安全地使用System.Threading.Monitor类型。上面的代码就直接等效于下面的代码:

private object o = new object();

public void Work()

{

  //这里很重要,是为了避免直接使用私有成员o,而导致线程不安全

  object temp = o;

  System.Threading.Monitor.Enter(temp);

  try

  {

    //做一些必要线程同步的工作

  }

  finally

  {

    System.Threading.Monitor.Exit(temp);

  }

}

正如你看到的,真正实现了线程同步功能的,就是System.Threading.Monitor类型,lock关键字只是用来代替调用Enter、Exit方法,并且将所有的工作包含在try块内,以保证其最终退出同步。

注意:我们lock的一般是对象,不是值类型和字符串。

1、为什么不能lock值类型

比如lock(1)呢?lock本质上Monitor.Enter,Monitor.Enter会使值类型装箱,每次lock的是装箱后的对象。lock 其实是类似编译器的语法糖,因此编译器直接限制住不能lock值类型。退一万步说,就算能编译器允许你lock(1),但是 object.ReferenceEquals(1,1)始终返回false(因为每次装箱后都是不同对象),也就是说每次都会判断成未申请互斥锁,这样 在同一时间,别的线程照样能够访问内里的代码,达不到同步的效果。同理lock((object)1)也不行。

2、Lock字符串

那么lock("xxx")字符串呢?MSDN上的原话是:

锁定字符串尤其危险,因为字符串被公共语言运行库 (CLR)“暂留”。 这意味着整个程序中任何给定字符串都只有一个实例,同一个对象表示了所有运行的应用程序域的所有线程中的该文本。因此,只要在应用程序进程中的任何 位置处具有相同内容的字符串上放置了锁,就将锁定应用程序中该字符串的所有实例。

3、MSDN推荐的Lock对象

通常,最好避免锁定 public 类型或锁定不受应用程序控制的对象实例。例如,如果该实例可以被公开访问,则 lock(this) 可能会有问题,因为不受控制的代码也可能会锁定该对象。这可能导致死锁,即两个或更多个线程等待释放同一对象。出于同样的原因,锁定公共数据类型(相比于 对象)也可能导致问题。

而且lock(this)只对当前对象有效,如果多个对象之间就达不到同步的效果。

而自定义类推荐用私有的只读静态对象,比如:

private static readonly object obj = new object();

为什么要设置成只读的呢?这是因为如果在lock代码段中改变obj的值,其它线程就畅通无阻了,因为互斥锁的对象变了,object.ReferenceEquals必然返回false。

  • Lock 关键字锁定静态变量和非静态变量的区别

181504oxpxqiaqqddin3zp.gif
181505xezalwtctlyl4wac.gif
  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. namespace testLock
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. Console.WriteLine("----------开始测试单实例非静态锁----------");
  14. MyLock myLock = new MyLock();
  15. for (int i = 0; i < 5; i++)
  16. {
  17. Thread t = new Thread(myLock.Increment1);
  18. t.Start();
  19. }
  20. Thread.Sleep(3 * 1000);
  21. Console.WriteLine("----------开始测试单实例静态锁----------");
  22. MyLock myLock2 = new MyLock();
  23. for (int i = 0; i < 5; i++)
  24. {
  25. Thread t = new Thread(myLock2.Increment2);
  26. t.Start();
  27. }
  28. Thread.Sleep(3 * 1000);
  29. Console.WriteLine("----------开始测试多实例非静态锁----------");
  30. for (int i = 0; i < 5; i++)
  31. {
  32. MyLock mlock = new MyLock();
  33. Thread t = new Thread(mlock.Increment1);
  34. t.Start();
  35. }
  36. Thread.Sleep(3 * 1000);
  37. Console.WriteLine("----------开始测试多实例静态锁----------");
  38. for (int i = 0; i < 5; i++)
  39. {
  40. MyLock mlock = new MyLock();
  41. Thread t = new Thread(mlock.Increment2);
  42. t.Start();
  43. }
  44. Console.Read();
  45. }
  46. }
  47. public class MyLock
  48. {
  49. //静态变量锁对象
  50. private readonly static object staticObj = new object();
  51. //非静态变量锁对象
  52. private readonly object obj = new object();
  53. //成员变量
  54. private static int i1 = 0;
  55. private static int i2 = 0;
  56. /// <summary>
  57. /// 非静态锁
  58. /// </summary>
  59. /// <param name="handleObject">要处理的对象</param>
  60. public void Increment1(object handleObject)
  61. {
  62. lock (obj)
  63. {
  64. Console.WriteLine("i1的值为:{0}", i1);
  65. //这里刻意制造线程并行时机,来检查同步的功能
  66. Thread.Sleep(200);
  67. i1++;
  68. Console.WriteLine("i1自增后为:{0}", i1);
  69. }
  70. }
  71. /// <summary>
  72. /// 静态锁
  73. /// </summary>
  74. /// <param name="handleObject">要处理的对象</param>
  75. public void Increment2(object handleObject)
  76. {
  77. lock (staticObj)
  78. {
  79. Console.WriteLine("i2的值为:{0}", i2);
  80. //这里刻意制造线程并行时机,来检查同步的功能
  81. Thread.Sleep(200);
  82. i2++;
  83. Console.WriteLine("i2自增后为:{0}", i2);
  84. }
  85. }
  86. }
  87. }
复制代码
View Code

单实例非静态锁,线程没有并发(加锁成功);

181505p589gesie88i9z48.png

单实例静态锁,线程没有并发(加锁成功);

181505ph1i08ccy2z8sl2n.png

多实例非静态锁,线程并发(加锁失败);

181506l7q5lsihqupahwhu.png

多实例静态锁,线程没有并发(加锁成功)

181506dg9z6zunfkgm22gf.png

说明:以上内容是根据网上内容进行整理,并加以归纳。





来源:https://www.cnblogs.com/HandsomeBlog/p/11357589.html
xiao123 | 2019-10-24 13:12:06 | 显示全部楼层
赞一个   赞一个  赞一个
[url=http://www.wxzbjx.com/bbxz/lgxt/m/2233.html]北京中科医院的刘云涛专家[/url]
剑弑 | 2019-10-25 17:28:16 | 显示全部楼层
对于lock的解释很详细66666,加上lock(this)为什么不建议使用就更好了。
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则