C# 锁汇总

打印 上一主题 下一主题

主题 914|帖子 914|积分 2742

一、前言
  本文章汇总c#中常见的锁,基本都列出了该锁在微软官网的文章,一些不常用的锁也可以参考微软文章左侧的列表,方便温习回顾。
二、锁的分类
2.1、用户模式锁
  1、volatile 关键字
  volatile 并没有实现真正的线程同步,操作级别停留在变量级别并非原子级别,对于单系统处理器中,变量存储在主内存中,没有机会被别人修改。但是如果是多处理器,可能就会有问题,因为每个处理器都有单独的data cache,数据更新不一定立刻被写回到主存,可能会造成不同步。
 参考:valatile 微软官网文章。
 
  2、Spinlock 旋转锁
   Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的,即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁则获取不到,只能够原地“打转”(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制 :自旋锁不应该被长时间的持有(消耗 CPU 资源)。
参考:Spinlock 微软官网文章。
 
2.2、内核模式锁
  1、事件锁
  自动事件锁:AutoResetEvent
  WaitOne()进入等待,Set()会释放当前锁给一个等待线程。
  1. var are = new AutoResetEvent(true);
  2. are.WaitOne();
  3. //...
  4. are.Set();
复制代码
 
  手动事件锁:ManualResetEvent
  WaitOne()进入等待,Set()会释放当前锁给所有等待线程。
  1. var mre = new ManualResetEvent(false);
  2. mre.WaitOne();//批量拦截,后续的省略号部分是无序执行的。
  3. //...
  4. mre.Set();//一次释放给所有等待线程
复制代码
参考:ManuaResetEvent 微软官网文章。

  2、信号量
  信号量:Semaphore
  信号量可以控制同时通过的线程数以及总的线程数。
  1. //第一个参数表示同时可以允许的线程数,比如1表示每次只允许一个线程通过,
  2. //第二个是最大值,比如8表示最多有8个线程。
  3. var semaphore = new Semaphore(1, 8);
复制代码
参考:Semaphore 微软官网文章。
 
  3、互斥锁
  互斥锁:Mutex
  Mutex和Monitor很接近,但是没有Monitor.Pulse,Wait,PulseAll的唤醒功能,他的优点是可以跨进程,可以在同一台机器甚至远程机器人的不同进程间共用一个互斥体。
  1. var mutex = new Mutex();
  2. mutex.WaitOne();
  3. //...
  4. mutex.ReleaseMutex();
复制代码
 参考:Mutex 微软官网文章。
 
  4、读写锁
  读写锁:ReaderWriterLock
  不要使用ReaderWriterLock,该类有问题(死锁、性能),请使用ReaderWriterLockSlim
.NET Framework有两个读取器-编写器锁,ReaderWriterLockSlim以及ReaderWriterLock。 建议对所有新开发的项目使用 ReaderWriterLockSlim。 虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。 ReaderWriterLockSlim 避免了许多潜在的死锁情况。 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLock。
参考:ReaderWriterLock 微软官网文章。
  
  读写锁:ReaderWriterLockSlim
  1. //源码摘录自微软官网
  2. using System;
  3. using System.Threading;
  4. using System.Threading.Tasks;
  5. using System.Collections.Generic;
  6. public class SynchronizedCache
  7. {
  8.     private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
  9.     private Dictionary<int, string> innerCache = new Dictionary<int, string>();
  10.     public int Count
  11.     { get { return innerCache.Count; } }
  12.     public string Read(int key)
  13.     {
  14.         cacheLock.EnterReadLock();
  15.         try
  16.         {
  17.             return innerCache[key];
  18.         }
  19.         finally
  20.         {
  21.             cacheLock.ExitReadLock();
  22.         }
  23.     }
  24.     public void Add(int key, string value)
  25.     {
  26.         cacheLock.EnterWriteLock();
  27.         try
  28.         {
  29.             innerCache.Add(key, value);
  30.         }
  31.         finally
  32.         {
  33.             cacheLock.ExitWriteLock();
  34.         }
  35.     }
  36.     public bool AddWithTimeout(int key, string value, int timeout)
  37.     {
  38.         if (cacheLock.TryEnterWriteLock(timeout))
  39.         {
  40.             try
  41.             {
  42.                 innerCache.Add(key, value);
  43.             }
  44.             finally
  45.             {
  46.                 cacheLock.ExitWriteLock();
  47.             }
  48.             return true;
  49.         }
  50.         else
  51.         {
  52.             return false;
  53.         }
  54.     }
  55.     public AddOrUpdateStatus AddOrUpdate(int key, string value)
  56.     {
  57.         cacheLock.EnterUpgradeableReadLock();
  58.         try
  59.         {
  60.             string result = null;
  61.             if (innerCache.TryGetValue(key, out result))
  62.             {
  63.                 if (result == value)
  64.                 {
  65.                     return AddOrUpdateStatus.Unchanged;
  66.                 }
  67.                 else
  68.                 {
  69.                     cacheLock.EnterWriteLock();
  70.                     try
  71.                     {
  72.                         innerCache[key] = value;
  73.                     }
  74.                     finally
  75.                     {
  76.                         cacheLock.ExitWriteLock();
  77.                     }
  78.                     return AddOrUpdateStatus.Updated;
  79.                 }
  80.             }
  81.             else
  82.             {
  83.                 cacheLock.EnterWriteLock();
  84.                 try
  85.                 {
  86.                     innerCache.Add(key, value);
  87.                 }
  88.                 finally
  89.                 {
  90.                     cacheLock.ExitWriteLock();
  91.                 }
  92.                 return AddOrUpdateStatus.Added;
  93.             }
  94.         }
  95.         finally
  96.         {
  97.             cacheLock.ExitUpgradeableReadLock();
  98.         }
  99.     }
  100.     public void Delete(int key)
  101.     {
  102.         cacheLock.EnterWriteLock();
  103.         try
  104.         {
  105.             innerCache.Remove(key);
  106.         }
  107.         finally
  108.         {
  109.             cacheLock.ExitWriteLock();
  110.         }
  111.     }
  112.     public enum AddOrUpdateStatus
  113.     {
  114.         Added,
  115.         Updated,
  116.         Unchanged
  117.     };
  118.     ~SynchronizedCache()
  119.     {
  120.        if (cacheLock != null) cacheLock.Dispose();
  121.     }
  122. }
复制代码
ReaderWriterLockSlim示例参考:ReaderWriterLockSlim 微软官网文章。
 
2.3、动态计数锁
  1、动态计数锁:CountdownEvent
  限制线程数的一个机制,而且这个也是比较常用的(同属于信号量的一种)。
  1. var cde = new CountdownEvent(10);
  2. //重置当前ThreadCount上限
  3. cde.Reset(10);
  4. for(int i=0; i<10; i++)
  5. {
  6.     Task.Factory.StartNew(()=>
  7.     {
  8.         Thread.Sleep(1000);
  9.         SubWoker1();
  10.     });
  11. }
  12. cde.Wait();//相当于Task.WaitAll()
  13. cde.Reset(8);
  14. for(int i=0; i<8; i++)
  15. {
  16.     Task.Factory.StartNew(()=>
  17.     {
  18.         Thread.Sleep(1000);
  19.         SubWoker2();
  20.     });
  21. }
  22. cde.Wait();//相当于Task.WaitAll()
  23. static void SubWoker1()
  24. {
  25.     //...
  26.     cde.Signal();//将当前的ThreadCount-1操作。
  27. }
  28. static void SubWoker2()
  29. {
  30.     //...
  31.     cde.Signal();//将当前的ThreadCount-1操作。
  32. }
  33.             
复制代码
CountdownEvent示例参考:CountdownEvent 微软官网文章。
 
  2、原子操作类:Interlocked
  Interlocked类则提供了4种方法进行原子级别的变量操作。Increment , Decrement , Exchange 和CompareExchange 。
  a、使用Increment 和Decrement 可以保证对一个整数的加减为一个原子操作。
  b、Exchange 方法自动交换指定变量的值。
  c、CompareExchange 方法组合了两个操作:比较两个值以及根据比较的结果将第三个值存储在其中一个变量中。
  d、比较和交换操作也是按原子操作执行的。Interlocked.CompareExchange(ref a, b, c); 原子操作,a参数和c参数比较, 相等b替换a,不相等不替换。
参考:Interlocked 微软官网文章。

2.4、监视锁
  1、监视锁:Monitor
  Monitor锁为操作的代码块添加互斥对象,如果A线程正在访问,对象没有到达临界区,则B线程不会访问。
 参考:Monitor 微软官网文章。
 
  2、监视锁:lock
  lock锁可以视为monitor锁的语法糖,增加了自动释放机制和异常处理机制。
  a、不推荐使用lock(this)的方式作为lock锁,因为你不确定别的地方是否重新实例了含有lock的对象。
  b、不要lock一个字符串。
  c、不要lock一个外部公开变量。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

慢吞云雾缓吐愁

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表