范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

使用c原子量实现自旋锁

  一、自旋锁
  自旋锁是一种基础的同步原语,用于保障对共享数据的互斥访问。与互斥锁的相比,在获取锁失败的时候不会使得线程阻塞而是一直自旋尝试获取锁。当线程等待自旋锁的时候,CPU不能做其他事情,而是一直处于轮询忙等的状态。自旋锁主要适用于被持有时间短,线程不希望在重新调度上花过多时间的情况。实际上许多其他类型的锁在底层使用了自旋锁实现,例如多数互斥锁在试图获取锁的时候会先自旋一小段时间,然后才会休眠。如果在持锁时间很长的场景下使用自旋锁,则会导致CPU在这个线程的时间片用尽之前一直消耗在无意义的忙等上,造成计算资源的浪费。二、CAS操作实现自旋锁
  CAS(Compare and Swap),即比较并替换,实现并发算法时常用到的一种技术,这种操作提供了硬件级别的原子操作(通过锁总线的方式)。CAS操作的原型可以认为是:bool CAS(V, A, B)
  其中V代表内存中的变量,A代表期待的值,B表示新值。当V的值与A相等时,将V与B的值交换。逻辑上可以用下面的伪代码表示:bool CAS(V, A, B) {     if (V == A)     {         swap(V, B);         return true;     }          return false; }
  需要强调的是上面的操作是原子的,要么不做,要么全部完成。
  那么已经拥有CAS操作的情况下如何实现一个自旋锁呢?首先回忆自旋锁的用途,本质上我们是希望能够让一个线程在不满足进入临界区的条件时,不停的忙等轮询,直到可以运行的时候再继续(进入临界区)执行。那么,我们可能自然的想到使用一个bool变量来表示是否可以进入临界区,例如以下面的伪代码的逻辑:while(flag == true); flag = true; /* do something ... */ flag = false;     ...
  这样做的直观想法是当flag为true的时候表示已经有线程处于临界区内,只有当flag为fasle时才能进入,而在进入的时候立即将flag置为true。但是这样做明显存在一个问题,判断flag为false和设置flag为true并不是一个不可分割的整体,有可能出现类似下面这样的时序, 假设最初flag为false:
  step
  thread 1
  thread 2
  step是虚构的步骤,do something为一系列指令,这里写在一起表示并发执行。这里可以看出由于thread1读取判断flag的值与修改flag的值是两个独立的操作,中间插入了thread2的判断操作,最终使得有两个线程同时进入了临界区,这与我们的期望相悖。那么如何解决呢?如果能将读取判断与修改的操作合二为一,变成一个不可分割的整体,那么自然就不可能出现这种交错的场景。对于这样一个整体操作,我们希望它能读取内存中变量的值,并且当其等于特定值的时候,修改它为我们需要的另一个值。嗯......没错,这样我们就得到了CAS操作。
  现在可以重新修改我们的同步方式,不停的进行期望flag为false的CAS操作 CAS(flag, flase, b) (这里b为true),直到其返回成功为止,再进行临界区中的操作,离开临界区时将flag置为false。b = true; while(!CAS(flag, false, b)); //do something flag = false;
  现在,判断操作与写入操作已经成为了一个整体,当一个线程的CAS操作成功的时候会阻止其他线程进入临界区,到达互斥访问的目的。
  现在我们已经可以使用CAS操作来解决临界区的互斥访问的问题了,但是如果每次都这样写一遍实在太过麻烦,因此可以进行一些封装使得使用更加方便,也就是说...可以封装成自旋锁。我们可以用一个类来表示,将一个bool值作为类的数据成员,同时将CAS操作和赋值操作作为其成员函数,CAS操作其实就是加锁操作,而后面的赋值操作就是解锁操作。三、用C++原子量实现
  按照上面的思路,接下来用 C++ 11 引入标准库的原子量来实现一个自旋锁并且进行测试。
  首先,我们需要一个bool值来表示锁的状态,这里直接使用标准库中的原子量 atomic (C++ 11的原子量可以参考:https://www.cnblogs.com/FateTHarlaown/p/8919235.html) ,在我的平台(Cygwin64、GCC7.3)上 atomic 的成员函数is_lock_free()返回值为true,是无锁的实现(如果内部使用了锁来实现的话那还叫什么自旋锁 = =)。实际上在大多数平台上 atomic都是无锁的,如果不确定的话也可以使用C++标准规定必须为无锁实现的atomic_flag。
  接下来,我们需要两个原子操作,CAS和赋值,C++11标准库在原子量的成员函数中直接提供了这两个操作。//CAS std::atomic::compare_exchange_weak( T& expected, T desired,                                     std::memory_order order =                                     std::memory_order_seq_cst ),                                      std::atomic::compare_exchange_strong( T& expected, T desired,                                     std::memory_order order =                                     std::memory_order_seq_cst ) //赋值 void store( T desired, std::memory_order order = std::memory_order_seq_cst )
  compare_exchange_weak 与 compare_exchange_strong 主要的区别在于内存中的值与expected相等的时候,CAS操作是否一定能成功,compare_exchange_weak有概率会返回失败,而compare_exchange_strong则一定会成功。因此,compare_exchange_weak必须与循环搭配使用来保证在失败的时候重试CAS操作。得到的好处是在某些平台上compare_exchange_weak性能更好。按照上面的模型,我们本来就要和while搭配使用,可以使用compare_exchange_weak。最后内存序的选择没有特殊需求直接使用默认的std::memory_order_seq_cst。而赋值操作非常简单直接,这个调用一定会成功(只是赋值而已 = =),没有返回值。
  实现代码非常短,下面是源代码:#include   class SpinLock {  public:     SpinLock() : flag_(false)     {}      void lock()     {         bool expect = false;         while (!flag_.compare_exchange_weak(expect, true))         {             //这里一定要将expect复原,执行失败时expect结果是未定的             expect = false;         }     }      void unlock()     {         flag_.store(false);     }  private:     std::atomic flag_; };
  如上面所说,lock操作不停的尝试CAS操作直到成功为止,unlock操作则将bool标志位复原。使用方式如下:SpinLock myLock; myLock.lock();  //do something  myLock.unlock();
  接下来,我们进行正确性测试,以经典的i++ 问题为例:#include  #include  #include   //自旋锁类定义 class SpinLock {  public:     SpinLock() : flag_(false)     {}      void lock()     {         bool expect = false;         while (!flag_.compare_exchange_weak(expect, true))         {             expect = false;         }     }      void unlock()     {         flag_.store(false);     }  private:     std::atomic flag_; };  //每个线程自增次数 const int kIncNum = 1000000; //线程数 const int kWorkerNum = 10; //自增计数器 int count = 0; //自旋锁 SpinLock spinLock; //每个线程的工作函数 void IncCounter() {     for (int i = 0; i < kIncNum; ++i)     {         spinLock.lock();         count++;         spinLock.unlock();     } }  int main() {     std::vector workers;     std::cout << "SpinLock inc MyTest start" << std::endl;     count = 0;      std::cout << "start " << kWorkerNum << " workers_" << "every worker inc " << kIncNum << std::endl;     std::cout << "count_: " << count << std::endl;     //创建10个工作线程进行自增操作     for (int i = 0; i < kWorkerNum; ++i)         workers.push_back(std::move(std::thread(IncCounter)));      for (auto it = workers.begin(); it != workers.end(); it++)         it->join();      std::cout << "workers_ end" << std::endl;     std::cout << "count_: " << count << std::endl;     //验证结果     if (count == kIncNum * kWorkerNum)     {         std::cout << "SpinLock inc MyTest passed" << std::endl;         return true;     }     else     {         std::cout << "SpinLock inc MyTest failed" << std::endl;         return false;     }      return 0; }
  上面的代码中创建了10个线程对共享的全局变量count分别进行一百万次++操作,然后验证结果是否正确,最终执行的输出为:SpinLock inc MyTest start start 10 workers_every worker inc 1000000 count_: 0 workers_ end count_: 10000000 SpinLock inc MyTest passed
  从结果中可以看出我们实现的自旋锁起到了保护临界区(这里就是i++ )的作用,count最后的值等于每个线程执行自增的数目之和。作为对比,可以去掉IncCounter中的加锁解锁操作:void IncCounter() {     for (int i = 0; i < kIncNum; ++i)     {         //spinLock.lock();         count++;         //spinLock.unlock();     } }
  执行后的输出为:SpinLock inc MyTest start start 10 workers_every worker inc 1000000 count_: 0 workers_ end count_: 7254522 SpinLock inc MyTest failed
  结果由于多个线程同时执行 i++ 造成结果错误。
  到这里,我们就通过 C++ 11的原子量实现了一个简单的自旋锁。这里只是对C++原子量的一个小使用,无论是自旋锁本身还是原子量都还有许多值得探究的地方。

晋能清洁能源科技股份公司软硬兼备降本增效近日,第六届异质结领跑量产与供应链配套协作国际论坛在无锡落幕。晋能控股电力集团晋能清洁能源科技股份公司(以下简称晋能科技)作为异质结技术领跑企业受邀出席活动,总经理杨立友发表演讲,映泰推出B660GTNITX主板支持12代酷睿DDR4内存映泰B660GTNITX主板现已上架,支持12代酷睿和DDR4内存,售价1199元,暂未到货。我们了解到,这款ITX主板采用了9相数字供电,配备两条DDR4内存插槽,支持DDR45电脑C盘严重不足?教你有效清理C盘空间,瞬间多出10GB很多小伙伴会发现一个问题,电脑使用的时间越久就会越来越卡,有时候开启浏览器都需要延迟,这是因为你的C盘内存不足所导致的,今天就来教大家如何彻底有效的清理爆满的C盘,让内存瞬间多出1郭明錤Apple的元宇宙头戴装置运算力领先竞争对手的产品约23年天风国际分析师郭明錤11日在其最新报告中表示,苹果的元宇宙头戴装置运算力领先竞争对手的产品约23年。苹果ARMR装置采用双ABF载板。每部苹果ARMR头戴装置将配备由4奈米与5奈米AYANEONext承诺年底将使用AMDRyzen6000系列CPU随着V社公布PC游戏掌机SteamDeck,已有其他公司加入这一市场分一杯羹。其中一个便是一家叫AyaNeo的小公司。他们公司的下一款产品AYANEONext设计理念和SteamD米物ART系列机械键盘Z870高颜值三模兼容超好用日常工作学习生活中,电脑是必备工具,电脑常用外设就是键盘和鼠标,常见的键盘有薄膜键盘和机械键盘,越来越多的用户选择使用机械键盘,而选择一款好用的机械键盘需要从键盘布局按键体验键盘轴不完整收录过去一年字节开源的10个项目丨字节技术年货祝全天下的开发者朋友,新年职业发展虎啸风生,新年工资增速如虎添翼,新年爱情运势虎跃龙骧,新年代码事故虎口逃生,虎!虎!虎!开源圣经大教堂与集市一书中曾言任何行业的成功几乎都直接和这C判定类型A是否能够基于隐式类型转换转为B类型?原文见C有哪些鲜为人知的奇特操作?知乎,直接看代码自定义两个内存大小不一样的类型,作为布尔值typedefcharTruetypedefstructchar2Falsetempla第七期深信服go实习一面二面HR面一面面试时长1h自我介绍channel知识点协程goroutinemysql的两种存储引擎InnoDB索引redis使用单线程还是多线程?有多少个库?redis持久化有哪些?各自优关于联想的原罪的联想随着虎年的到来,全社会关于联想问题的大讨论已成为往年的事了。相信人们不会忘记联想的,特别是教父借助联想的所作所为,更期待官方对此的结论至少是态度。纵观联想问题讨论或争议的最重要焦点欠债122亿的乐视,又整活了哈哈哈哈。春节,大伙最乐此不疲的大概就是,各种薅羊毛抢红包了吧。这不,新春来临之际,互联网各大APP又在红包上卷起来了。支付宝5亿京东15亿快手22亿百度22亿抖音20亿。好家伙,互联网资本
宜人贷推荐装饰器(Decorator),它对金融数据平台不可或缺随着数据时代的来临,数据量和数据复杂度的增长推进了网站平台工程领域的快速发展。为了满足不同客户的数据获取等需求,众多企业使出浑身解数,完善自身系统功能。对于网站的完善性,装饰器(D基金收评丨医疗新能源下跌,后市行情如何?收评开启今天的最终战绩为上证指数0。83,深证成指1。60,创业板指2。25,根据今天的表现,少然财经团队的分析师发布主题美国5月会加息吗?对A股有何影响?针对这个主题,团队的分析盘点油车与新能源车各自的好处92加满,吓得腿软,95加满,倾家荡产,98加满,三代还款,而随着新能源车的崛起,人们可以选择的车型越来越多,今天我们就来说说燃油车和新能源各自的优势在哪,希望能给各位购车带来些指各大车企纷纷宣布停止生产燃油车时间,燃油车被新能源汽车替代2022年四月3日比亚迪汽车宣布自2022年3月起停止燃油车的整车生产但是不会停止生产燃油车的配件继续为已购买燃油车车主提供售后保养服务比亚迪的这一宣布无疑就是带动整个汽车行业停止甘肃出台方案加强科技创新11日,甘肃省政府常务会议通过了甘肃省强科技行动实施方案(20222025年)(以下简称方案),力争到2025年,科技进步贡献率综合科技创新水平指数企业研发经费支出占全社会研发经费51TalkCOO张礼明辞职,曾被徐小平称为中国教培产业职业经理第一人记者管丢丢日前,51Talk发布公告称,公司联合创始人兼首席运营官(COO)张礼明因个人原因提出辞职,并将于2022年5月15日正式卸任COO一职。面对国内双减政策,在线教育机构不广发资管携手感易智能升级战心智能预警平台本报记者李乔宇4月12日,由广发资管战心投研团队与感易智能联合打造的投研全量舆情信息整合分析平台战心智能预警平台再次完成了优化升级,旨在从业务层面优化标签指标的丰富度,从效果层面提真相腾讯裁员30,结果走的可能是最优秀的那部分员工腾讯裁员30!裁员的重要依据,就是过往的绩效评星!但是在腾讯内部的考核体系中,评星强制分布。即一个团队必须要有低星成员,即便团队全员表现都很好都优秀,也要有人强制性为团队背低星。但加密货币界的PayPal!MoonPay最新轮融资获一众名人入局智通财经APP获悉,加密货币支付公司MoonPay在周三的一份新闻稿中表示,在公司的最新一轮融资中,共有60多名包括音乐家演员等在内的名人为该公司提供了约8700万美元的资金,占该特斯拉中国召回刷新速度纪录,但看完德国的退钱,我酸了那个刹不住的特斯拉Model3,仿佛已经淡出人们的视野好久了,刹车门事件基本上已经偃旗息鼓。但是,最近特斯拉Model3强势回归,从刹车门变成了失速门。这一次特斯拉到是没有东拉西扯Smart首款纯电SUV现身,奔驰联手吉利打造,轴距2米75,6。7S破百要说吉利之所以能有如今的强大号召力,一方面固然有其在汽车板块的强势发挥。而另一方面,恐怕也与其不断扩张的商业版图所带来的舆论热度不无关系。毕竟,在轰动一时的收购沃尔沃之后,吉利还成