最新要闻
- 世界热消息:谷歌报复性砸出5620亿参数大模型:比ChatGPT更恐怖 学术圈已刷屏
- 【环球播资讯】2月国产游戏出海成绩出炉:《原神》获收入和增长双料冠军
- 有关马的歇后语有哪些?有关马的古诗有哪些?
- 工科理科化现象亟待扭转!曹德旺等科学家企业喊话让学生去工厂一线真问题
- 【世界时快讯】委员喊话农村淘汰、封杀老头乐 网友吵翻:揭秘观点背后让人唏嘘?
- 隐婚男女的结局是什么?隐婚男女演员介绍
- 小学二年级班主任工作计划有哪些?小学二年级家长会发言稿
- 消防逃生的注意事项有哪些?消防逃生演练总结
- 旅游可持续发展的实质是什么?旅游可持续发展论文模板
- 英语六级考试时间安排分配是什么?英语六级考试题型简介
- 世界热头条丨虼蚤的读音是什么_虼蚤
- 描写景色的词语集锦有哪些?描写景色的段落摘抄
- 梁祯元为什么叫南韩贾宝玉?梁祯元为什么是队长?
- 中国相术十二宫都有哪些?相术十二宫实用顺口溜
- 田宅宫在脸上的什么位置?田宅宫代表什么?
- 剑指Notion:微软协作平台Loop即将进入公开预览阶段
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
金三银四每天一个.NET基础知识巩固(一)
一. 什么是线程安全?
正式一点的说法是:指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。 talk is cheap,show me the code.直接上代码理解:
int i = 10, number = 99;while (i>0){ Console.WriteLine(++number); i--;}
结果会输出什么?我们刚接触编程语言的同学都能看出来,number变量会依次+1并且打印:
(资料图片)
那如果我们使用多线程来对共享变量number进行累加输出呢?
int i = 10, number = 99;while (i > 0){ Task.Run(() => { Console.WriteLine(++number); }); i--;}复制代码
结果会输出什么?
看起来毫无规律可循,为什么会发生这种情况?
这是因为++number的指令在实际执行的过程中不是原子性的,而是要分为读、改、写三步来进行;即先从内存中读出number的值,然后执行+1操作,再将结果写回内存中。也就是说,可能线程一在读取number的值之后,还没来得及改,CPU切换到线程二读取了number的值,并且修改后写入内存中,这个时候如果再切回线程一,继续修改写入,那么这两次操作得到的值就是一样的,和我们预期逻辑不符。
那么,我们该如何避免这种情况,来保证线程安全呢?
1.原子操作
现在一般的CPU可以使用总线锁和缓存锁来实现: CPU在硬件上可以通过拉低引线来实现总线锁,当需要进行一个原子操作时,CPU就可以拉低引线,将总线锁住,实现其他CPU与内存之间的通讯隔离,这样,其他的CPU就不能够从内存中读取数据了。 由于总线锁会中断其他CPU与内存之间的通讯,导致在这段时间内其他CPU访问不到内存,效率降低,因此总线锁的开销是很大的,于是又出现了一种新的方式,缓存锁。
缓存锁不会阻断CPU与内存之间的通讯,内存中的一个变量可以被多个核心访问,其缓存行有被修改的、独享的、共享的、无效的这四种状态,简单的来说,在某个CPU修改变量时,会将其他CPU中的缓存置为无效的,从而保证在修改时,每个CPU核心中的值都是最新的。
C#中的原子操作在C#中,提供了Interlocked类来实现原子操作,其方法有Add、Increment、Decrement、Exchange、CompareExchange等,可以使用原子操作进行加法、加一、减一、替换、比较替换等操作。 那么我们优化一下刚才的代码,是不是就打印正常了呢?
int i = 10, number = 99;while (i > 0){ Task.Run(() => { Interlocked.Increment(ref number); Console.WriteLine(++number); }); i--;}复制代码
答案是,打印依旧是没有规律的,这是为什么? 因为在我们Task的匿名委托里面,我们需要执行的是两个方法,我们使用Interlocked类确实保证了之前++number这个操作的原子性,但是Console.WriteLine方法我们并没有保证,所以会出现我们执行完number的修改后,还没有输出,线程却被挂起,被其他线程抢先进行了输出,但我们可以发现,我们输出的十个数没有重复的,这并不是巧合,恰恰是因为我们使用了Interlocked保证了number变量读取修改写入的原子性。
C#的线程安全类型
C#提供了一些线程安全的数据类型,这些数据类型大量应用了无锁算法来提升访问速度,如:System.Collections.Consurrent.CurrentBag
System.Collections.Consurrent.CurrentDictionary
System.Collections.Consurrent.CurrentQueue
System.Collections.Consurrent.CurrentStack
但是如果我就是想要task里的全部操作都是线程安全的,那怎么办呢?这就需要线程锁了。
C#的线程锁
自旋锁
自旋锁(Spinlock)是最简单的线程锁,基于原子操作实现。它使用一个数值来表示锁是否已经被获取,0表示未被获取,1表示已经获取。获取锁时会先使用原子操作设置数值为1,然后检查修改前的值是否为0,如果为0则代表获取成功,否则继续重试直到成功为止。 释放锁时会设置数值为0,其他正在获取锁的线程会在下一次重试时成功获取。 使用原子操作的原因是,它可以保证多个线程同时把数值0修改到1时,只有一个线程可以观察到修改前的值为0,其他线程观察到修改前的值为1。
.NET 可以使用以下的类实现自旋锁:
System.Threading.Thread.SpinWait
System.Threading.SpinWait
System.Threading.SpinLock
使用自旋锁有个需要注意的问题,自旋锁保护的代码应该在非常短的时间内执行完毕,如果代码长时间运行则其他需要获取锁的线程会不断重试并占用逻辑核心,影响其他线程运行。
此外,如果 CPU 只有一个逻辑核心,自旋锁在获取失败时应该立刻调用 Thread.Yield 函数提示操作系统切换到其他线程,因为一个逻辑核心同一时间只能运行一个线程,在切换线程之前其他线程没有机会运行,也就是切换线程之前自旋锁没有机会被释放。 我们使用System.Threading.SpinLock来优化一下刚才的代码:
int i = 10, number = 99;var sl = new SpinLock();while (i > 0){ Task.Run(() => { var getlock = false; sl.Enter(ref getlock); Console.WriteLine(++number); if (getlock) sl.Exit(); }); i--;}复制代码
结果正常输出:
互斥锁
由于自旋锁不适用于长时间运行,它的使用场景比较有限,更通用的线程锁是操作系统提供的基于原子操作与线程调度实现的互斥锁(Mutex)。与自旋锁一样,操作系统提供的互斥锁内部有一个数值表示是否已经被获取,不同的是当获取锁失败时,它不会反复重试,而是安排获取锁的线程进入等待状态,并把线程对象添加到锁关联的队列中,另一个线程释放锁时会检查队列中是否有线程对象,如果有则通知操作系统唤醒该线程。
因为处于等待状态的线程没有运行,即使长时间不释放也不会消耗 CPU 资源,但让线程进入等待状态与从等待状态唤醒并调度运行可能会花费毫秒级的时间,与自旋锁重试所需的纳秒级时间相比非常的长,所以在笔者看来,单进程内自旋锁和互斥锁的选择就是一种时间与空间的选择。
C#提供了 System.Threading.Mutex 类,这个类包装了操作系统提供的互斥锁,它是可重入的,已经获取锁的线程可以再次执行获取锁的操作,但释放锁的操作也要执行相同的次数,可重入的锁又叫递归锁(Recursive Lock)
递归锁内部使用一个计数器记录进入次数,同一个线程每获取一次就加1,释放一次就减1,减1后如果计数器变为0就执行真正的释放操作,一般用在嵌套调用的多个函数中
Mutex 类的另一个特点是支持跨进程使用,创建时通过构造函数的第二个参数可以传入名称
如果一个进程获取了锁,那么在释放该锁前的另一个进程获取同样名称的锁需要等待;如果进程获取了锁,但是在退出之前没有调用释放锁的方法,那么锁会被操作系统自动释放,其他当前正在等待锁(锁被自动释放前进入等待状态)的进程会收到 AbandonedMutexException 异常
跨进程锁通常用于保护多个进程共享的资源或者防止程序多重启动
我们使用Mutex类优化一下刚才的代码:
int i = 10, number = 99;var mut = new Mutex();while (i > 0){ Task.Run(() => { mut.WaitOne(); Console.WriteLine(++number); mut.ReleaseMutex(); }); i--;}复制代码
其中可以通过对Mutex构造函数的重载传入互斥锁的锁名,进行跨进程的使用,这里就不演示了,输出的结果和使用自旋锁一样,是正常的。
混合锁
互斥锁 Mutex 使用时必须创建改类型的实例,因为实例包含了非托管的互斥锁对象,开发者必须在不使用锁后尽快调用 Dispose 函数释放非托管资源,并且因为获取锁失败后会立刻安排线程进入等待,总体上性能比较低
.NET 提供了更通用而且更高性能的混合锁(Monitor),任何引用类型的对象都可以作为锁对象,不需要事先创建指定类型的实例,并且涉及的非托管资源由 .NET 运行时自动释放,不需要手动调用释放函数
获取和释放混合锁需要使用 System.Threading.Monitor 类中的函数
C# 提供了 lock 语法糖来简化通过 Monitor 类获取和释放的代码
混合锁的特征是在获取锁失败后像自旋锁一样重试一定的次数,超过一定次数之后(.NET Core 2.1 是30次)再安排当前进程进入等待状态
混合锁的好处是,如果第一次获取锁失败,但其他线程马上释放了锁,当前线程在下一轮重试可以获取成功,不需要执行毫秒级的线程调度处理;而如果其他线程在短时间内没有释放锁,线程会在超过重试次数之后进入等待状态,以避免消耗 CPU 资源,因此混合锁适用于大部分场景。
我们继续优化刚才的代码:
int i = 10, number = 99;var obj = new Object();while (i > 0){ Task.Run(() => { lock(obj) { Console.WriteLine(++number); } }); i--;}复制代码
结果自然也是正常的,是不是感觉lock语法糖比之前的更加简便?
读写锁
读写锁(ReaderWriterLock)是一个具有特殊用途的线程锁,适用于频繁读取且读取需要一定时间的场景。
共享资源的读取操作通常是可以同时执行的,而普通的互斥锁不管是读取还是修改操作都无法同时执行,如果多个线程为了读取操作而获取互斥锁,那么同一时间只有一个线程可以执行读取操作,在频繁读取的场景下会对吞吐量造成影响。
读写锁分为读取锁和写入锁,线程可以根据对共享资源的操作类型选择获取读写锁还是写入锁,读取锁可以被多个线程同时获取,写入锁不可以被多个线程同时获取,而且读取锁和写入锁不可以被不同的线程同时获取。
C#提供的 System.Threading.ReaderWriterLockSlim 类实现了读写锁,
读写锁也是一个混合锁(Hybird Lock),在获取锁时通过自旋重试一定的次数再进入等待状态
此外,它还支持同一个线程先获取读写锁,然后再升级为写入锁,适用于“需要先获取读写锁,然后读取共享数据判断是否需要修改,需要修改时再获取写入锁”的场景
关键词:
-
今日要闻!从“13 天”到“0 天”延时,揭秘火山引擎 DataLeap SLA 保障最佳实践
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群在数据治理中,...
来源: 环球今亮点!快 40 岁,刚被裁。。
金三银四每天一个.NET基础知识巩固(一)
今日要闻!从“13 天”到“0 天”延时,揭秘火山引擎 DataLeap SLA 保障最佳实践
世界热消息:谷歌报复性砸出5620亿参数大模型:比ChatGPT更恐怖 学术圈已刷屏
【环球播资讯】2月国产游戏出海成绩出炉:《原神》获收入和增长双料冠军
Nginx http 文件服务器 中文名称文件乱码以及不能访问下载问题 (解决全过程)
有关马的歇后语有哪些?有关马的古诗有哪些?
工科理科化现象亟待扭转!曹德旺等科学家企业喊话让学生去工厂一线真问题
【世界时快讯】委员喊话农村淘汰、封杀老头乐 网友吵翻:揭秘观点背后让人唏嘘?
隐婚男女的结局是什么?隐婚男女演员介绍
小学二年级班主任工作计划有哪些?小学二年级家长会发言稿
消防逃生的注意事项有哪些?消防逃生演练总结
旅游可持续发展的实质是什么?旅游可持续发展论文模板
英语六级考试时间安排分配是什么?英语六级考试题型简介
世界热头条丨虼蚤的读音是什么_虼蚤
描写景色的词语集锦有哪些?描写景色的段落摘抄
梁祯元为什么叫南韩贾宝玉?梁祯元为什么是队长?
中国相术十二宫都有哪些?相术十二宫实用顺口溜
田宅宫在脸上的什么位置?田宅宫代表什么?
Linux 上的开源视频字幕应用–Live Captions
全球信息:Win10专业版激活方法
【环球播资讯】kafka常用指令
剑指Notion:微软协作平台Loop即将进入公开预览阶段
当前滚动:又来一个“保时捷” 江汽EV3申报:国内首搭载46系列大圆柱电芯
160g超满足:嘉兴特产蛋黄大肉粽2.9元/只大促
环球播报:从“看不起”到“跟不上”:200多名理想汽车车主分享用车体验
热头条丨《街霸6》新解说员宣传片:日本少女冠军人美声甜!
新消息丨国内“投教第一股”九方财富登陆港股,业绩亮眼,市值逼近80亿
面向状态机编程:复杂业务逻辑应对之道
多光源渲染方案 - Many Lights Sampling
世界微动态丨在java中String类为什么要设计成final?Java面试常见问题
报道:LoadRunner——脚本优化(二)
马斯克要自建“乌托邦小镇”:员工全部搬进去 自己当“镇长”
拒绝投影行业亮度虚标!Vidda官宣三色激光全家桶新品
环球最资讯丨新一轮国内油价将于17日迎来调整:有再度搁浅可能
【环球聚看点】彻底解决“刹车争议”!电商平台上线特斯拉脚部专用记录仪:全程摄像
当前播报:简单到复杂:C#拷贝文件的3种方法
环球热门:对LSTM应用于图像的初步理解
【数论与组合数学 1】数论简介、素数、算数基本定理
JS回调地狱
天天视讯!GTX 1050 Ti就能跑!顽皮狗公布《最后生还者:Part 1》PC版配置要求
世界看点:自称12年驾龄 特斯拉Model X车主在线维权:踩刹车没反应加速撞柱子
天天观察:苹果古典音乐软件已上架:Apple Music会员免费用!中国市场随后推出
当前资讯!明基推出首款48寸OLED电竞显示器:4K 120Hz、90W反向供电
《生化危机4:重制版》PS5版疑似已偷跑 小心剧透啊
世界百事通!illustrator学习心得体会(illustrator序列号)
工厂模式进阶用法,如何动态选择对象?
迷你天猫商城代码审计
焦点简讯:K8S 性能优化 - K8S APIServer 调优
【全球聚看点】Prompt-Engineering-Guide 学习摘要1
前端设计模式——装饰者模式
65寸4K大屏电视不到2000元 LCD白菜价即将结束:3月价格上涨10%
环球微头条丨最强AI再次进化 ChatGPT下周升级GPT-4:支持视频了
【环球聚看点】免费玩!《生化危机4:重制版》体验版上线:不限时、不限次
当前滚动:德国电动空中出租车Lilium jet完成测试:时速250km/h 全机36个电风扇
杀疯了!长安深夜放大招 购车百亿补贴:深蓝SL03直降2.2万
世界快看点丨分享几个常用的运维 shell 脚本
世界观点:佳兆业成今年首家复牌出险房企
全球球精选!一座河南小县城的全球钻石生意爆火:价格不到天然的1/3
我国再次成功发射一箭双星:天绘六号A/B星顺利进入预定轨道
当前快看:资助8年的女生毕业放弃工作 嫁给有钱人成家庭主妇 资助人:失眠好几天
天天通讯!上班族如何备考公务员_如何备考公务员
全球焦点!读Java性能权威指南(第2版)笔记12_堆内存中
怎么处理消息重发的问题?
每日热点:HEU KMS Activator 30.0.0全能系统数字许可激活工具(全新体验纪念版)
环球热议:用盆吃10袋泡面男子火了 回应月薪2万邀约:浇完家里18亩地再说
实时:第127篇:异步函数(async和await)练习题(异步,消息队列)
焦点!【LeetCode回溯算法#05】分割回文串(复习双指针判断回文以及substr函数使用记录)
今日热议:【django-vue】celery延迟任务、定时任务 django中使用celery 秒杀功能 双写一致性 首页轮播图定时更新 课程前端页面
世界热头条丨关于JAVA泛型数组类型擦除引发的问题及解决方案
环球今日讯!Mint安装MySQL
快讯:苹果iPhone 14黄色款预售:全新配色不加价 5999元起
天天微速讯:租客辞职要搬走被房东介绍工作 当事人感动
即时焦点:一辆腾势敢占两个充电桩:真不怕挨揍?
“常温超导”把A股都晃晕了!真要改变我们的生活?
《霍格沃茨之遗》渗人面部Bug:玩家看了起鸡皮疙瘩
全球短讯!美环保署署长:将在今年最终确定甲烷排放规则
21世纪20年代是几几年(21世纪20年代是几几年)
爆肝两万字,详解fastdfs分布式文件系统
初识rollup 打包、配置vue脚手架
世界热议:62.类模板
当前热文:C语言——可变参函数
陕西招聘会现3万月薪岗位学生排长龙 招聘人员:半天收简历150份
当前动态:供不应求!真我GT Neo5 1TB版真香:二手用户也抢着要
“山药成了精”?男子买到奇葩山药外形酷似人脚掌
每日视点!快速读懂Redis分布式锁的实现和原理
美少女三消游戏 《Mirror 2: Project X》开发组宣布破产解散
酒店回应到211大学招服务员:符合流程 面向所有高校毕业生
快看点丨京东CEO徐雷:百亿补贴效果超过预期、要做天天低价
【新要闻】比亚迪加入降价大军!宋Pro DM-i限时优惠:88元折扣6888元
焦点简讯:耳机煲机一般要煲多久_耳机煲机方法是什么?买回来新耳机要怎样煲?耳机要煲多久?
世界热点评!前端如何相对优雅管理api
全球速看:浙四医院官网招聘2021_浙四医院官网
环球观点:京东2022年收入超1万亿:“百亿补贴”会一直有!
简约时尚 健身备一件:361°新款轻薄速干衣39元冲量
越来越卖不动了!最畅销十款数码相机一览:索尼成赢家 第一性价比绝
全球通讯!Intel的大小核CPU架构:最终还是把一些老游戏坑了
往返近1000元!景区回应坐滑竿上山按斤收费:网友力挺明码标价
天天实时:【翻译】发布 .NET 8 Preview 1
前沿资讯!海洋风筝爆火 一周暴涨498%超过秋裤!网友:春天的信号