C#中lock 和 ReaderWriterLock 的使用总结

线程锁是多线程并发共享数据,保证一致性的工具。多线程可以同时运行多个任务但是当多个线程同时访问共享数据时,可能导致数据不同步。当有多个线程访问同一对象的加锁方法或代码块时,同一时间只有一个线程在执行,其余线程必须要等待当前线程执行完之后才能执行该代码段。但其余线程是可以访问该对象中的非加锁代码块的。以下介绍.NET(C#)中 lock 和 ReaderWriterLock 的使用。

 

1、lock 和 ReaderWriterLock

lock 语句获取给定对象的互斥 lock,执行语句块,然后释放 lock。 持有 lock 时,持有 lock 的线程可以再次获取并释放 lock。 阻止任何其他线程获取 lock 并等待释放 lockReaderWriterLock支持单个写线程和多个读线程的锁。.NET Framework 有两个读取器-编写器锁, ReaderWriterLockSlimReaderWriterLock 。 建议对所有新开发的项目使用 ReaderWriterLockSlim。 虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。 ReaderWriterLockSlim 避免了许多潜在的死锁情况。 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLockReaderWriterLock长时间持有读取器锁或写入器锁将枯竭其他线程。 为了获得最佳性能,请考虑重构应用程序以最大程度地缩短写入的持续时间。

lock的使用语法:

lock (x)
{// 加锁代码
}

 lock是语法糖,代码相当于:

object __lockObj = x;
bool __lockWasTaken = false;
try
{System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);// 加锁代码
}
finally
{if (__lockWasTaken) System.Threading.Monitor.Exit(__lockObj);
}

 

注意:无论lock锁定的是this,还是obj,只要关心多线程锁定的对象是不是为同一个对象。当同步对共享资源的线程访问时,最好使用锁定专用对象实例(例如,private readonly object balanceLock = new object();),避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用。最好避免使用this、Type 实例、字符串实例作为lock锁定的对象。

2、lock的使用

lock为互斥锁,lock 关键字将语句块标记为临界区,方法是获取给定对象的互斥锁,执行语句,然后释放该锁。当任何一个线程获取到锁后,其他线程如果需要使用该临界区内代码,则必须等待前一个线程使用完毕后释放锁。

例如,

using System;
using System.Threading.Tasks;
namespace ConsoleApplication
{class Program{static void Main(){var account = new Account(1000);var tasks = new Task[100];for (int i = 0; i < tasks.Length; i++){tasks[i] = Task.Run(() => Update(account));}Task.WhenAll(tasks).Wait();Console.WriteLine($"Account balance : {account.GetBalance()}");Console.ReadKey();}static void Update(Account account){decimal[] amounts = { 0, 2, -3, 6, -2, -1, 8, -5, 11, -6 };foreach (var amount in amounts){if (amount >= 0){account.Credit(amount);}else{account.Debit(Math.Abs(amount));}}}}public class Account{private readonly object balanceLock = new object();private decimal balance;public Account(decimal initialBalance) => balance = initialBalance;public decimal Debit(decimal amount){if (amount < 0){throw new ArgumentOutOfRangeException(nameof(amount), "The debit amount cannot be negative.");}decimal appliedAmount = 0;lock (balanceLock){if (balance >= amount){balance -= amount;appliedAmount = amount;}}return appliedAmount;}public void Credit(decimal amount){if (amount < 0){throw new ArgumentOutOfRangeException(nameof(amount), "The credit amount cannot be negative.");}lock (balanceLock){balance += amount;}}public decimal GetBalance(){lock (balanceLock){return balance;}}}
}

 

3、ReaderWriterLock的使用

ReaderWriterLock为读写锁,ReaderWriterLock 定义支持单个写线程和多个读线程的锁。该锁主要是解决并发读的性能问题,使用该锁可以大大提高数据并发访问的性能,只有在写时,才会阻塞所有的读锁。建议对所有新开发的项目使用 ReaderWriterLockSlim。 虽然 ReaderWriterLockSlim 类似于 ReaderWriterLock,但不同之处在于,前者简化了递归规则以及锁状态的升级和降级规则。 ReaderWriterLockSlim 避免了许多潜在的死锁情况。 另外,ReaderWriterLockSlim 的性能显著优于 ReaderWriterLock

例如,

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication
{class Program{static ReaderWriterLock rwl = new ReaderWriterLock();// 定义ReaderWriterLock对象。static int resource = 0;const int numThreads = 8;static bool running = true;static int readerTimeouts = 0;static int writerTimeouts = 0;static int reads = 0;static int writes = 0;public static void Main(){// 启动多个线程,对共享资源进行随机读写。Thread[] t = new Thread[numThreads];for (int i = 0; i < numThreads; i++){t[i] = new Thread(new ThreadStart(ThreadProc));t[i].Name = new String(Convert.ToChar(i + 65), 1);t[i].Start();if (i > 10)Thread.Sleep(300);}// 等待它们全部完成。running = false;for (int i = 0; i < numThreads; i++)t[i].Join();Console.WriteLine("\nread:{0} , write:{1} , reader time-out:{2}, writer time-out:{3}",reads, writes, readerTimeouts, writerTimeouts);Console.Write("Press ENTER to exit... ");Console.ReadLine();}static void ThreadProc(){Random rnd = new Random();//随机选择线程对共享资源进行读写的方式。while (running){double action = rnd.NextDouble();if (action < .8)ReadFromResource(10);else if (action < .81)ReleaseRestore(rnd, 50);else if (action < .90)UpgradeDowngrade(rnd, 100);elseWriteToResource(rnd, 100);}}// 请求和释放读取锁,并处理超时。static void ReadFromResource(int timeOut){try{rwl.AcquireReaderLock(timeOut);try{// 这个线程从共享资源读取是安全的。Display("reads resource value :" + resource);Interlocked.Increment(ref reads);}finally{// 确保锁已释放。rwl.ReleaseReaderLock();}}catch (ApplicationException){// 读取锁定请求超时处理Interlocked.Increment(ref readerTimeouts);}}// 请求和释放写入锁,并处理超时static void WriteToResource(Random rnd, int timeOut){try{rwl.AcquireWriterLock(timeOut);try{// 这个线程从共享资源访问是安全的resource = rnd.Next(500);Display("writes resource value " + resource);Interlocked.Increment(ref writes);}finally{// 确保锁已释放rwl.ReleaseWriterLock();}}catch (ApplicationException){// 写锁请求超时处理Interlocked.Increment(ref writerTimeouts);}}// 请求读取锁,将读取锁升级为写入锁,然后再次将其降级为读取锁。static void UpgradeDowngrade(Random rnd, int timeOut){try{rwl.AcquireReaderLock(timeOut);try{// 这个线程从共享资源读取是安全的Display("reads resource value " + resource);Interlocked.Increment(ref reads);//要写资源,要么释放读锁,要么请求写锁,或升级读锁升级//读取锁将线程放入写队列中,在any后面可能正在等待写入锁的其他线程。try{LockCookie lc = rwl.UpgradeToWriterLock(timeOut);try{//对这个线程来说,从共享资源读写是安全的。resource = rnd.Next(500);Display("writes resource value " + resource);Interlocked.Increment(ref writes);}finally{// 确保锁已释放。rwl.DowngradeFromWriterLock(ref lc);}}catch (ApplicationException){// 事件解释升级请求超时。Interlocked.Increment(ref writerTimeouts);}// 如果锁被降级,从资源中读取仍然是安全的。Display("reads resource value: " + resource);Interlocked.Increment(ref reads);}finally{// 确保锁已释放rwl.ReleaseReaderLock();}}catch (ApplicationException){// 读取锁定请求超时处理步骤Interlocked.Increment(ref readerTimeouts);}}//释放所有锁,之后恢复锁状态。//使用序列号来确定另一个线程是否有//获得了一个写锁,因为该线程最后一次访问资源。static void ReleaseRestore(Random rnd, int timeOut){int lastWriter;try{rwl.AcquireReaderLock(timeOut);try{//线程从共享资源中读取数据是安全的,//读取和缓存资源的值。int resourceValue = resource;     // 缓存资源值。Display("reads resource value " + resourceValue);Interlocked.Increment(ref reads);// 保存当前写入器序列号。lastWriter = rwl.WriterSeqNum;// 释放锁并保存一个cookie,以便稍后可以恢复锁。LockCookie lc = rwl.ReleaseLock();// 等待一个随机的时间间隔,然后恢复之前的锁状态。Thread.Sleep(rnd.Next(250));rwl.RestoreLock(ref lc);//检查其他线程是否在这个时间间隔内获得写锁。//如果不是,则资源的缓存值仍然有效。if (rwl.AnyWritersSince(lastWriter)){resourceValue = resource;Interlocked.Increment(ref reads);Display("resource has changed: " + resourceValue);}else{Display("resource has not changed: " + resourceValue);}}finally{// 确保锁已释放。rwl.ReleaseReaderLock();}}catch (ApplicationException){// 读取锁定请求超时处理Interlocked.Increment(ref readerTimeouts);}}static void Display(string msg){Console.Write("Thread {0} {1}.       \r", Thread.CurrentThread.Name, msg);}}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/108723.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

新加坡服务器托管

新加坡是一个小而繁荣的国家&#xff0c;是东南亚唯一一个发达国家。它地理位置好&#xff0c;毗邻马来西亚和印度尼西亚&#xff0c;新加坡是一个拥有先进科技和强大经济的国家&#xff0c;主要以制造业、金融、旅游和航运为主&#xff0c;拥有先进的经济和现代化的基础设施&a…

C++进阶篇1---继承

一、继承的概念和定义 1.1概念 继承机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称为派生类。继承呈现了面向对象程序设计的层次结构&#xff…

AFL模糊测试+GCOV覆盖率分析

安全之安全(security)博客目录导读 覆盖率分析汇总 目录 一、代码示例 二、afl-cov工具下载 三、编译带覆盖率的版本并启动afl-cov 四、AFL编译插桩并运行afl-fuzz 五、结果查看 AFL相关详见AFL安全漏洞挖掘 GCOV相关详见GCOV覆盖率分析 现将两者结合&#xff0c;即进…

添加Mybatis框架支持

配置环境 1.老项目 在pom.xml中使用generate ->edit starts->添加下面俩 2.新项目 然后运行发现会有报错 2.1这是因为没有配置数据库连接信息 spring:datasource:url: jdbc:mysql://localhost:3306/java2023?characterEncodingutf8&useSSLfalse # MySQL数…

PDF编辑阅读:Acrobat Pro DC 2021中文稳定版

Acrobat Pro DC 2021是一款专业的PDF编辑和阅读软件。它可以创建、编辑、组合、签署和分享PDF文件&#xff0c;提供了许多强大的功能&#xff0c;如PDF文件转换、OCR识别、PDF文件合并、加密和解密等等。Acrobat Pro DC 2021的界面简单直观&#xff0c;易于使用&#xff0c;而且…

四.镜头知识之放大倍率

四.镜头知识之放大倍率 文章目录 四.镜头知识之放大倍率4.0 前言4.1 镜头的光学放大倍率的计算方法4.2 显示器的电子放大倍率4.2.1 智能硬件产品的显示放大倍率计算案例4.3 系统放大倍率4.4 智能硬件产品的系统放大倍率计算案例4.4 智能硬件产品的系统放大倍率计算案例4.0 前言…

为什么学校互联网专业教的只是出社会都没用?

今日话题&#xff0c;为什么很多学生都觉得认真学习了学校课程&#xff0c;但是出社会企业发现一点用都没有&#xff1f;解决方法放在了后方&#xff0c;免费领取。首先&#xff0c;计算机技术的迅猛发展导致学校教材和课程更新滞后&#xff0c;可能学到的知识已是老旧。嵌入式…

JNDI-Injection-Exploit工具安装

从github上下载安装 git clone https://github.com/welk1n/JNDI-Injection-Exploit.git 打开 cd JNDI-Injection-Exploit 编译安装&#xff0c;Maven入门百科_maven中quickstart是什么意思-CSDN博客 mvn clean package -DskipTests 因为提示mvn错误&#xff0c;解决下…

leetcode-200. 岛屿数量

1. 题目 leetcode题目链接 2. 解答 思路&#xff1a; 需要循环遍历每个节点&#xff1b;找到陆地&#xff0c;基于陆地开始遍历陆地的上下左右&#xff1b;数组dirm dirn就可以表示某个区域的上下左右&#xff1b;标记遍历过的节点&#xff1b;设计循环的退出条件&#xf…

Mybatis Available parameters are [0, 1, param1, param2]解决方法

Mybatis Available parameters are [0, 1, param1, param2]解决方法 原因 这与Mybatis的参数匹配机制有关&#xff0c;当传递多个参数的时候&#xff0c;映射机制并不清楚如何匹配到正确的参数 方法一&#xff1a;使用Param参数 方法二&#xff1a;使用Map传参数

图论-最短路径算法-弗洛伊德算法与迪杰斯特拉算法

弗洛伊德算法&#xff1a; 弗洛伊德算法本质是动态规划&#xff0c;通过添加点进如可选择的点组成的集合的同时更新所有点之间的距离&#xff0c;从而得到每两个点之间的最短距离。 初始化&#xff1a; 创建一个二维数组 dist&#xff0c;其中 dist[i][j] 表示从节点 i 到节点…

嵌入式实时操作系统的设计与开发(调度策略学习)

将调度分为两层&#xff0c;上层为策略&#xff0c;下层为机制&#xff0c;并且采用策略与机制分离的设计原则&#xff0c;可以方便灵活地扩展调度策略&#xff0c;而不改变底层的调度机制。 调度策略就是如何确定线程的CPU、优先级prio等参数&#xff0c;线程是按照FIFO&…

【LeetCode刷题(数据结构)】:检查两颗树是否相同

给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true 输入&#xff1a;p [1,2], q [1,…

SpringBoot原理初探

pom.xml 父依赖 核心的依赖都在父工程中&#xff01; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version><relativePath/> <!…

05_51单片机led流水线的实现

1:step创建一个新的项目并将程序烧录进入51单片机 以下是51单片机流水线代码的具体实现 #include <REGX52.H>void Delay500ms() //11.0592MHz {unsigned char i, j, k;i 4;j 129;k 119;do{do{while (--k);} while (--j);} while (--i); }void main(){while(1){P1 0…

【性能测试篇1】初识性能测试

目录 性能测试定义 性能测试和功能测试有什么区别 测试工具上面&#xff1a; 特殊业务场景下&#xff1a; 性能测试常见概念&#xff1a; ①用户相关&#xff1a; 1.1并发用户数&#xff1a; 1.2在线用户数&#xff1a; 1.3系统用户数量&#xff1a; ②响应时间相关&…

SpringMVC之国际化上传下载

spring项目中的国际化 1&#xff09;提供中英两种资源文件 i18n_en_US.properties i18n_zh_CN.properties 2&#xff09;配置国际化资源文件&#xff08;在spring配置文件中添加&#xff0c;例如spring-mvc.xml&#xff09; <bean id"messageSource" class&quo…

通达OA 2016网络智能办公系统 handle.php SQL注入漏洞

一、漏洞描述 北京通达信科科技有限公司通达OA2016网络智能办公系统 handle.php 存在sql注入漏洞&#xff0c;攻击者可利用此漏洞获取数据库管理员权限&#xff0c;查询数据、获取系统信息&#xff0c;威胁企业单位数据安全。 二、网络空间搜索引擎查询 fofa查询 app"T…

【STL】平衡二叉树

前言 对于之前普通的二叉搜索树&#xff0c;其搜索的效率依靠树的形状来决定&#xff0c;如下&#xff1a; 可以看到 A图 中的树比较彭亨&#xff0c;搜索一个元素的效率接近 O(logN) &#xff1b;而 B图 中的形状也符合搜索二叉树&#xff0c;但是很不平衡&#xff0c;这时的…

框架篇

一、Spring中的单例Bean是线程安全的吗 二、AOP相关面试题 三、Spring中的事务 四、Spring中事务失效的场景有 五、Spring bean的生命周期 六、Spring的循环依赖 七、SpringMVC的执行流程 八、自动配置原理 九、Spring框架常见的注解 十、Mybatis的执行流程 十一、MyBatis延迟加…