当前位置: 首页 > news >正文

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);}}
}

 

相关文章:

C#中lock 和 ReaderWriterLock 的使用总结

线程锁是多线程并发共享数据&#xff0c;保证一致性的工具。多线程可以同时运行多个任务但是当多个线程同时访问共享数据时&#xff0c;可能导致数据不同步。当有多个线程访问同一对象的加锁方法或代码块时&#xff0c;同一时间只有一个线程在执行&#xff0c;其余线程必须要等…...

Mac下通过nvm管理node

背景 本地有两个项目&#xff0c;老项目需要用到node 14&#xff0c;新项目需要用node 16&#xff0c;所以只能通过nvm来管理node了 卸载原始的node 我的node是通过官网的.pkg文件安装的&#xff0c;可以通过以下命令进行删除 sudo rm -rf /usr/local/{bin/{node,npm},lib/…...

易点易动固定资产管理系统:RFID出入监控,保障固定资产安全

在企业管理中&#xff0c;固定资产的安全和管理一直是一项重要的任务。企业往往面临着固定资产丢失、盗窃和不当使用等问题&#xff0c;给企业带来巨大的经济损失和管理难题。为了解决这些问题&#xff0c;我们推出了易点易动固定资产管理系统&#xff0c;结合RFID出入监控技术…...

Vue封装组件并发布到npm仓库

1. 环境准备 因为我们此次封装的是Vue组件&#xff0c;所以我们直接在Vue脚手架项目里面进行封装即可。 &#xff08;1&#xff09;初始化Vue项目 vue create lin-vue &#xff08;2&#xff09;运行项目 npm run serve 2. 组件封装 新建src/components文件夹 因为我们可…...

python+深度学习+opencv实现植物识别算法系统 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…...

基于springboot实现医院急诊平台系统项目【项目源码】

基于springboot实现医院急诊平台系统演示 Spring Boot框架 Spring Boot是Pivotal团队的一个新框架&#xff0c;旨在简化新Spring应用程序的初始设置和开发。该框架使用特定的配置方法&#xff0c;无需开发人员定义样板配置。通过这种方式&#xff0c;Spring Boot旨在成为蓬勃发…...

【02】基础知识:React - jsx语法规则

一、jsx 简介 全称为JavaScript XML&#xff0c;是 react 定义的一种类似于 XML 的 JS 扩展语法 JS XML 本质是 React.createElement(component, props, …children) 方法的语法糖&#xff0c;用来简化创建虚拟 DOM 写法&#xff1a;var ele <h1>Hello JSX!</h1&…...

C语言 —— 指针

目录 1. 指针是什么&#xff1f; 2. 指针和指针类型的关系 2.1 指针的解引用 2.2 指针-整数 3. 野指针 3.1 野指针成因 1. 指针未初始化 2. 指针越界访问 3. 指针指向的空间释放 3.2 如何规避野指针 4. 指针运算 4.1 指针-整数 4.2 指针-指针 指针-指针的使用 4.3 指针的关系运…...

淘宝店铺所有商品数据接口,淘宝整店所有商品数据接口,淘宝店铺商品接口,淘宝API接口

淘宝店铺所有商品数据接口可以通过淘宝开放平台获取。以下是具体步骤&#xff1a; 在开放平台注册成为开发者并创建一个应用&#xff0c;获取到所需的 App Key 和 App Secret 等信息。使用获取到的 App Key 和 App Secret 进行签名和认证&#xff0c;获取 Access Token。调用开…...

【Redis】Java客户端使用zset命令

zadd/zrange zcard zrem zscore zrank...

记录一个@Transaction注解引发的bug

记录一个Transactional(readOnly true)注解引发的bug 一、问题代码和报错 1-1 问题代码模拟 引发这个问题的三大要素分别是&#xff1a; 事务注解任意数据库操作数据库操作后执行耗时业务&#xff08;耗时超过数据库配置的超时时间&#xff09; //1.这里是问题的核心之一…...

解决docker使用pandarallel报错OSError: [Errno 28] No space left on device

参考&#xff1a;https://github.com/nalepae/pandarallel/issues/127 在使用pandarallel报错OSError: [Errno 28] No space left on device&#xff0c;根据上述issue发现确实默认使用的MEMORY_FS_ROOT为 /dev/shm&#xff0c;而在docker环境下这个目录大小只有64M&#xff0…...

Javascript自定义页面复制事件

Javascript自定义页面复制事件 – WhiteNights Site 2023年10月13日 文章访问量&#xff1a;90 标签&#xff1a;Javascript 监听copy事件以达到自定义页面复制功能的效果。 写者注 需要注意的是&#xff0c;浏览器的部分拓展插件&#xff08;如迅雷&#xff09;会导致本文…...

Nginx:反向代理(示意图+配置)

示意图&#xff1a; 反向代理 反向代理&#xff08;Reverse Proxy&#xff09;是代理服务器的一种&#xff0c;它代表服务器接收客户端的请求&#xff0c;并将这些请求转发到适当的服务器。当请求在后端服务器完成之后&#xff0c;反向代理搜集请求的响应并将其传输给客户端。…...

macbook笔记本电脑内存怎么清理才能干净流畅?

假如你还在为“你的系统内存不足”的提示所困扰&#xff0c;或者你的Mac电脑突然运行缓慢和卡顿&#xff0c;那么你一般需要认真了解一下macbook内存怎么清理了? MacBook是功能强大的电脑&#xff0c;这点毫无疑问&#xff0c;但是它仍旧会随着时间推移变得运行缓慢。值得庆幸…...

spark 与 mapreduce 对比

Spark 为什么比 MapReduce 快总结 首先澄清几个误区&#xff1a; 1&#xff09;两者都是基于内存计算的&#xff0c;任何计算框架都肯定是基于内存的&#xff0c;所以说网上所说的 Spark 是基于内存计算所以快&#xff0c;显然是错误的。 2&#xff09;DAG 计算模型减少的是磁…...

kafka 相关概念

1 kafka 生产者 kafka 用push的方式把消息推送到topic 每个topic下可以有多个分区&#xff0c; 可以用hash 也可以用轮询的方式指定分区 每个分区内部是可以保证顺序的&#xff0c;但是整体无法保证顺序&#xff0c;除非设置成一个topic只有一个分区。 kafka这种多分区的设置 带…...

Ubuntu下vscode配置OpenCV以及Libtorch

opencv安装 sudo apt-get updatesudo apt-get install libopencv-dev 该方式安装的版本可能比较旧。 测试代码 #include <opencv2/opencv.hpp>#include <iostream>int main() {cv::Mat image cv::imread("t.png");cv::imshow("Image", ima…...

关于共识算法Raft的常见误解

关于共识算法Raft的常见误解 Raft 共识算法最终一致性与线性一致性日志的覆盖与删除Remove节点时需要skip 总结参考文档 Raft 共识算法 最近翻了翻Raft相关的资料&#xff0c;同时也总结了日常工作的一些积累&#xff0c;就当做Raft技术笔记吧。 由于工作的关系&#xff0c;Ra…...

Python学习基础笔记七十——模块和库1

模块和库&#xff1a; 一个python代码文件就实现了功能。功能比较单一。 在企业中&#xff0c;项目开发的文件&#xff0c;可能有成百上千个。 不同的代码文件&#xff0c;实现了不同的功能模块&#xff0c;就像一块块积木一样。这些功能文件整合起来&#xff0c;实现一个完…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...