首页 > 编程语言 >C# 锁汇总

C# 锁汇总

时间:2022-11-15 14:59:30浏览次数:57  
标签:微软 C# 汇总 线程 ReaderWriterLockSlim key 官网 cacheLock

一、前言

  本文章汇总c#中常见的锁,基本都列出了该锁在微软官网的文章,一些不常用的锁也可以参考微软文章左侧的列表,方便温习回顾。

二、锁的分类

2.1、用户模式锁

  1、volatile 关键字

  volatile 并没有实现真正的线程同步,操作级别停留在变量级别并非原子级别,对于单系统处理器中,变量存储在主内存中,没有机会被别人修改。但是如果是多处理器,可能就会有问题,因为每个处理器都有单独的data cache,数据更新不一定立刻被写回到主存,可能会造成不同步。

 参考:valatile 微软官网文章。

 

  2、Spinlock 旋转锁

   Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是“原地等待”的方式解决资源冲突的,即,一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁则获取不到,只能够原地“打转”(忙等待)。由于自旋锁的这个忙等待的特性,注定了它使用场景上的限制 :自旋锁不应该被长时间的持有(消耗 CPU 资源)。

参考:Spinlock 微软官网文章。

 

2.2、内核模式锁

  1、事件锁

  自动事件锁:AutoResetEvent

  WaitOne()进入等待,Set()会释放当前锁给一个等待线程。

var are = new AutoResetEvent(true);
are.WaitOne();
//...
are.Set();

 

  手动事件锁:ManualResetEvent

  WaitOne()进入等待,Set()会释放当前锁给所有等待线程。

var mre = new ManualResetEvent(false);

mre.WaitOne();//批量拦截,后续的省略号部分是无序执行的。
//...
mre.Set();//一次释放给所有等待线程

参考:ManuaResetEvent 微软官网文章。


  2、信号量

  信号量:Semaphore

  信号量可以控制同时通过的线程数以及总的线程数。

//第一个参数表示同时可以允许的线程数,比如1表示每次只允许一个线程通过,
//第二个是最大值,比如8表示最多有8个线程。
var semaphore = new Semaphore(1, 8);

参考:Semaphore 微软官网文章。

 

  3、互斥锁

  互斥锁:Mutex

  Mutex和Monitor很接近,但是没有Monitor.Pulse,Wait,PulseAll的唤醒功能,他的优点是可以跨进程,可以在同一台机器甚至远程机器人的不同进程间共用一个互斥体。

var mutex = new Mutex();
mutex.WaitOne();
//...
mutex.ReleaseMutex();

 参考:Mutex 微软官网文章。

 

  4、读写锁

  读写锁:ReaderWriterLock

  不要使用ReaderWriterLock,该类有问题(死锁、性能),请使用ReaderWriterLockSlim

.NET Framework有两个读取器-编写器锁,ReaderWriterLockSlim以及ReaderWriterLock。 建议对所有新开发的项目使用 ReaderWriterLockSlim。 虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。 ReaderWriterLockSlim 避免了许多潜在的死锁情况。 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLock。

参考:ReaderWriterLock 微软官网文章。

  

  读写锁:ReaderWriterLockSlim

//源码摘录自微软官网
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;

public class SynchronizedCache 
{
    private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
    private Dictionary<int, string> innerCache = new Dictionary<int, string>();

    public int Count
    { get { return innerCache.Count; } }

    public string Read(int key)
    {
        cacheLock.EnterReadLock();
        try
        {
            return innerCache[key];
        }
        finally
        {
            cacheLock.ExitReadLock();
        }
    }

    public void Add(int key, string value)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Add(key, value);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public bool AddWithTimeout(int key, string value, int timeout)
    {
        if (cacheLock.TryEnterWriteLock(timeout))
        {
            try
            {
                innerCache.Add(key, value);
            }
            finally
            {
                cacheLock.ExitWriteLock();
            }
            return true;
        }
        else
        {
            return false;
        }
    }

    public AddOrUpdateStatus AddOrUpdate(int key, string value)
    {
        cacheLock.EnterUpgradeableReadLock();
        try
        {
            string result = null;
            if (innerCache.TryGetValue(key, out result))
            {
                if (result == value)
                {
                    return AddOrUpdateStatus.Unchanged;
                }
                else
                {
                    cacheLock.EnterWriteLock();
                    try
                    {
                        innerCache[key] = value;
                    }
                    finally
                    {
                        cacheLock.ExitWriteLock();
                    }
                    return AddOrUpdateStatus.Updated;
                }
            }
            else
            {
                cacheLock.EnterWriteLock();
                try
                {
                    innerCache.Add(key, value);
                }
                finally
                {
                    cacheLock.ExitWriteLock();
                }
                return AddOrUpdateStatus.Added;
            }
        }
        finally
        {
            cacheLock.ExitUpgradeableReadLock();
        }
    }

    public void Delete(int key)
    {
        cacheLock.EnterWriteLock();
        try
        {
            innerCache.Remove(key);
        }
        finally
        {
            cacheLock.ExitWriteLock();
        }
    }

    public enum AddOrUpdateStatus
    {
        Added,
        Updated,
        Unchanged
    };

    ~SynchronizedCache()
    {
       if (cacheLock != null) cacheLock.Dispose();
    }
}
ReaderWriterLockSlim示例

参考:ReaderWriterLockSlim 微软官网文章。

 

2.3、动态计数锁

  1、动态计数锁:CountdownEvent

  限制线程数的一个机制,而且这个也是比较常用的(同属于信号量的一种)。

var cde = new CountdownEvent(10);

//重置当前ThreadCount上限
cde.Reset(10);
for(int i=0; i<10; i++)
{
    Task.Factory.StartNew(()=>
    {
        Thread.Sleep(1000);
        SubWoker1();
    });
}

cde.Wait();//相当于Task.WaitAll()

cde.Reset(8);
for(int i=0; i<8; i++)
{
    Task.Factory.StartNew(()=>
    {
        Thread.Sleep(1000);
        SubWoker2();
    });
}
cde.Wait();//相当于Task.WaitAll()

static void SubWoker1()
{
    //...
    cde.Signal();//将当前的ThreadCount-1操作。
}

static void SubWoker2()
{
    //...
    cde.Signal();//将当前的ThreadCount-1操作。
}


            
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一个外部公开变量。

 

标签:微软,C#,汇总,线程,ReaderWriterLockSlim,key,官网,cacheLock
From: https://www.cnblogs.com/lucienbao/p/lock.html

相关文章

  • CANN 6.0来了,硬核技术抢先看
    摘要:在华为全联接大会2022期间,华为正式官宣昇腾AI异构计算架构CANN6.0版本将在年底正式发布。本文分享自华为云社区《昇腾AI异构计算架构CANN6.0全新开放升级,全面释放AI......
  • 初学C语言
    注意事项:1)C程序的主体结构说明:#include...viodmain(){//{}包括内容,成为函数体语句1;语句2}2)源文件以“c”为扩展名3)执行入口是main()方法4)严格区分大小写5)每句以......
  • JavaScript 如何判断一个对象中是否有某个属性?
    今天讲讲,JavaScript如何判断一个对象中是否有某个属性?我总结了5个方法: 方法1:if(Obj[a]){}缺点:对于参数值为 undefined 和 0 的无效。方法2:if(ainObj){......
  • umi配置chainWebpack,使用自定义loader----jsx-px2rem
    前言虽然云谦大佬在github上说了,umi本身的配置已经很完善了,但是肯定满足不了所有人各种各样的奇葩需求。。。比如今天说的将jsx中的style里,将px转换为rem。 umi本身提......
  • 5.docker pxc多机多节点搭建
    1.拉取pxc镜像dockerpullpercona/percona-xtradb-cluster2.如果嫌弃名称太长,修改镜像名称(可不做这一步)dockertagpercona/percona-xtradb-clusterpxc删除原始镜......
  • Word11 工程学院讲师论文office真题
    1.根据题目一的要求,打开素材文件,点击【文件】-【另存为】,选择【当前文件夹】,命名为Word。   2.根据题目二的要求,根据提供的参考样式,打开Word的文档,在【布局】里点击......
  • SB20100LCT-ASEMI低压降肖特基二极管SB20100LCT
    编辑:llSB20100LCT-ASEMI低压降肖特基二极管SB20100LCT型号:SB20100LCT品牌:ASEMI封装:TO-220AB正向电流:20A反向电压:100V引线数量:3芯片个数:2芯片尺寸:82MIL漏电流:10ua......
  • Elasticsearch-head安装
    1.安装node要安装elasticsearch-head插件,需要先安装node.js。官网下载地址:https://nodejs.org/en/download/或者使用命令行安装wgethttps://npm.taobao.org/mirrors......
  • What is Owned Entity? When and why to use Owned Entity in Entity Framework Core?
    WhatisOwnedEntity?WhenandwhytouseOwnedEntityinEntityFrameworkCore?回答1Whatdoesthislooklikewithoutownedentities?Ifyoucreateanentit......
  • The entity type 'Vehicle' cannot be configured as owned because it has already b
    System.InvalidOperationException:Theentitytype'Vehicle'cannotbeconfiguredasownedbecauseithasalreadybeenconfiguredasanon-owned.Ifyouwantto......