专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

Mutex互斥锁设计思路详解和源码探究

  序言
  Mutex通常用于并发环境下控制多个协程对临界资源的访问,使得并发的协程之间可以互斥地操作同一份资源。这篇文章我们就来理解一下Golang中互斥锁的设计思想以及对互斥锁的实现源码进行探究。
  之前发布过一篇简短的解析,感觉不够详细因此重新发一篇文章深入剖析一下。源码解析
  下面是源码中Mutex的定义,其实现了Locker接口。AMutexisamutualexclusionlock。ThezerovalueforaMutexisanunlockedmutex。AMutexmustnotbecopiedafterfirstuse。typeMutexstruct{stateint32semauint32}ALockerrepresentsanobjectthatcanbelockedandunlocked。typeLockerinterface{Lock()Unlock()}const(mutexLocked1iotamutexislockedmutexWokenmutexStarvingmutexWaiterShiftiotaMutexfairness。Mutexcanbein2modesofoperations:normalandstarvation。InnormalmodewaitersarequeuedinFIFOorder,butawokenupwaiterdoesnotownthemutexandcompeteswithnewarrivinggoroutinesovertheownership。NewarrivinggoroutineshaveanadvantagetheyarealreadyrunningonCPUandtherecanbelotsofthem,soawokenupwaiterhasgoodchancesoflosing。Insuchcaseitisqueuedatfrontofthewaitqueue。Ifawaiterfailstoacquirethemutexformorethan1ms,itswitchesmutextothestarvationmode。Instarvationmodeownershipofthemutexisdirectlyhandedofffromtheunlockinggoroutinetothewaiteratthefrontofthequeue。Newarrivinggoroutinesdonttrytoacquirethemutexevenifitappearstobeunlocked,anddonttrytospin。Insteadtheyqueuethemselvesatthetailofthewaitqueue。Ifawaiterreceivesownershipofthemutexandseesthateither(1)itisthelastwaiterinthequeue,or(2)itwaitedforlessthan1ms,itswitchesmutexbacktonormaloperationmode。Normalmodehasconsiderablybetterperformanceasagoroutinecanacquireamutexseveraltimesinarowevenifthereareblockedwaiters。Starvationmodeisimportanttopreventpathologicalcasesoftaillatency。starvationThresholdNs1e6)源码注释翻译
  Mutex是互斥锁。零值状态下是非上锁状态,第一次使用后不允许再被拷贝。
  Mutex有两种工作模式:正常模式和饥饿模式。
  在正常模式下,所有的goroutine会按照先进先出的顺序等待锁的释放。但是一个刚被唤醒的goroutine不会直接获取到锁,而是会和新来的请求锁的goroutine去竞争锁。新来的goroutine具有一个优势:它正在CPU上执行而且可能有好几个新来的goroutine同时在请求锁,所以刚刚被唤醒的goroutine有很大可能在锁竞争中失败。在这种情况下,这个被唤醒的goroutine在竞争锁失败之后会加入到等待队列的最前面。如果一个等待的goroutine超过1ms后仍然没有获取锁,那么它将会把锁转变为饥饿模式。
  在饥饿模式下,锁的所有权将从执行unlock的goroutine直接交给等待队列中的第一个goroutine。新来的goroutine将不能再去尝试竞争锁,即使锁是unlock状态也不会去进行自旋操作,而是进入等待队列的尾部等待被唤醒。
  如果一个被唤醒的goroutine获取到了锁,并且满足以下其中一个条件,那么他会将锁换为正常工作模式:a。它是等待队列中的最后一个。b。它等待的时间小于1ms。
  正常模式具有较好的性能,因为一个goroutine可以连续多次获取锁,即使还有其他的阻塞等待锁的goroutine也不需要进入休眠阻塞。饥饿模式对防止尾部延迟的问题是非常重要的。解读
  可以看到golang将Mutex设计成了两种工作模式,默认初始化时是正常模式,这种模式下一个请求锁的groutine将会有很高的效率获取到锁。饥饿模式下会优先将锁给到那些等待时间最久的协程。为什么这么设计
  其实从注释中我们就可以解读出这么设计的原因,那就是为了提高加锁效率。
  正常来说,如果是我们自己设计一个锁,必定是先尝试获取锁,如果获取失败则进入等待队列休眠等待锁释放时被唤醒。这样每当锁释放时都要唤醒一个协程。被唤醒的协程等待runtime调度并且协程切换才能继续获取锁,比较耗费性能,如果此时存在正在运行中的请求锁的协程,那就可以让该协程优先获取锁,提高加锁的效率(所谓来的早不如来的巧)。显而易见,这种行为在某些情况下会导致处于等待中的协程长时间获取不到锁的情况,因此需要设计饥饿模式,在该模式下被释放的锁会优先给到饥饿的协程,这样就保证了锁的公平性。
  在理解了Mutex的设计思想之后我们再去看源码的具体实现,按照我们上面的思路去逐一分析关键代码。定义typeMutexstruct{stateint32semauint32}const(mutexLocked1iota加锁标识位掩码mutexWoken唤醒标识位掩码mutexStarving饥饿标识位掩码mutexWaiterShiftiota休眠等待协程计数起始位的偏移)state是个复合变量,用不同bit位标识锁的当前状态。
  第0位标识是否被加锁。m。statemutexLocked!0表示锁被持有。第1位标识是否存在被唤醒的协程。m。statemutexWoken!0表示存在被唤醒的协程。第2位标识锁当前所处的工作模式。m。statemutesStarving!0表示锁处于饥饿模式。331位用于记录处于休眠等待的协程的数量。countm。statemutexWaiterShift。
  sema是信号量,用于等待goroutine的唤醒操作。加锁Locklocksm。Ifthelockisalreadyinuse,thecallinggoroutineblocksuntilthemutexisavailable。如果Mutex已经处于加锁状态,那么调用Lock函数的协程将会阻塞直到加锁成功。func(mMutex)Lock(){Fastpath:grabunlockedmutex。如果锁是初始化状态则尝试加锁,成功直接返回ifatomic。CompareAndSwapInt32(m。state,0,mutexLocked){ifrace。Enabled{race。Acquire(unsafe。Pointer(m))}return}Slowpath(outlinedsothatthefastpathcanbeinlined)否则调用lockSlow去和其他协程竞争锁。m。lockSlow()}func(mMutex)lockSlow(){varwaitStartTimeint64当前协程阻塞等待的开始时间starving:false当前协程是否饥饿awoke:false当前协程是否是被唤醒的iter:0当前协程已经自旋的次数old:m。state获取当前锁的状态for{用于自旋以及被唤醒后的逻辑处理Dontspininstarvationmode,ownershipishandedofftowaiterssowewontbeabletoacquirethemutexanyway。饥饿模式下是直接将锁交给下一位,所以我们这里是不可以获取锁的,所以只有正常模式可以自旋,饥饿模式直接阻塞。正常模式在锁没有释放的情况下自旋一段时间,如果期间锁被释放能更快的获取到锁。ifold(mutexLockedmutexStarving)mutexLockedruntimecanSpin(iter){Activespinningmakessense。TrytosetmutexWokenflagtoinformUnlocktonotwakeotherblockedgoroutines。自旋时尝试设置唤醒位来告诉Unlock操作不要再唤醒阻塞的协程,因为已经有正在运行的协程了,所以就不要再多余唤醒。(这个实现就是满足我们上面理解的优先将锁给到正在执行中的协程的设计)这里需要满足3个条件:1。如果当前协程是被唤醒的或者当前协程已经成功设置了woken位,就没必要再设置了。2。如果已经有被唤醒的协程就没必要再设置了。3。如果没有在阻塞等待的协程也就没必要设置了,因为unlock本身也不会执行唤醒操作。if!awokeoldmutexWoken0oldmutexWaiterShift!0atomic。CompareAndSwapInt32(m。state,old,oldmutexWoken){awoketrue}runtimedoSpin()iteroldm。statecontinue}走到这里就有三种情况1。锁已经被释放,可以竞争锁了2。超出自旋次数限制且锁还没释放,需要排队等待3。锁已经处于饥饿模式,需要排队等待1和2两种情况都处在正常模式下,均可以去竞争锁,情况3只能休眠。new:oldDonttrytoacquirestarvingmutex,newarrivinggoroutinesmustqueue。非饥饿模式下可以尝试获取锁ifoldmutexStarving0{newmutexLocked}饥饿模式下只能进等待队列了ifold(mutexLockedmutexStarving)!0{new1mutexWaiterShift}Thecurrentgoroutineswitchesmutextostarvationmode。Butifthemutexiscurrentlyunlocked,dontdotheswitch。Unlockexpectsthatstarvingmutexhaswaiters,whichwillnotbetrueinthiscase。如果当前协程已经饥饿且锁还被持有则将锁切换为饥饿模式。如果当前锁已释放就不要切换,因为饥饿模式下必须要有等待者(因为饥饿模式下解锁时会直接把锁给到等待的协程),这种情况下不一定有等待的协程。ifstarvingoldmutexLocked!0{newmutexStarving}ifawoke{Thegoroutinehasbeenwokenfromsleep,soweneedtoresettheflagineithercase。ifnewmutexWoken0{throw(sync:inconsistentmutexstate)}如果当前协程是被唤醒的,需要重置唤醒位newmutexWoken}ifatomic。CompareAndSwapInt32(m。state,old,new){状态设置成功如果当前锁是释放状态且工作在正常模式下则加锁成功。ifold(mutexLockedmutexStarving)0{breaklockedthemutexwithCAS}当前可能状态有:锁处于饥饿模式或者还在被持有中Ifwewerealreadywaitingbefore,queueatthefrontofthequeue。如果waitStartTime!0说明是被唤醒的,则重新进入队列的头部等待下次被唤醒。queueLifo:waitStartTime!0ifwaitStartTime0{waitStartTimeruntimenanotime()}runtimeSemacquireMutex(m。sema,queueLifo,1)阻塞排队判断是否已经饥饿starvingstarvingruntimenanotime()waitStartTimestarvationThresholdNsoldm。stateifoldmutexStarving!0{Ifthisgoroutinewaswokenandmutexisinstarvationmode,ownershipwashandedofftousbutmutexisinsomewhatinconsistentstate:mutexLockedisnotsetandwearestillaccountedaswaiter。Fixthat。如果当前协程被唤醒且Mutex处于饥饿模式,锁的拥有者将锁交到当前协程但是锁处于不一致的状态:mutexLocked还没有被设置并且当前协程还被算成一个等待者,需要修复状态。ifold(mutexLockedmutexWoken)!0oldmutexWaiterShift0{throw(sync:inconsistentmutexstate)}delta:int32(mutexLocked1mutexWaiterShift)if!starvingoldmutexWaiterShift1{Exitstarvationmode。Criticaltodoithereandconsiderwaittime。Starvationmodeissoinefficient,thattwogoroutinescangolockstepinfinitelyoncetheyswitchmutextostarvationmode。如果当前为非饥饿模式或者自己是等待队列里的最后一个,则调整锁模式为正常模式deltamutexStarving}atomic。AddInt32(m。state,delta)break}非饥饿模式下被唤醒后重新竞争锁,没有优势awoketrueiter0}else{oldm。state竞争失败重来一次}}ifrace。Enabled{race。Acquire(unsafe。Pointer(m))}}快速加锁
  atomic。CompareAndSwapInt32(m。state,0,mutexLocked)用于判断锁是否为初始状态并尝试加锁,如果加锁成功则直接返回,提高加锁效率;如果加锁失败说明锁已被占用,则调用lockSlow去做相应的操作。自旋满足下面条件能进入自旋状态:
  1。当前互斥锁的状态是非饥饿状态,并且已经被锁定了。
  2。自旋次数不超过4次。
  3。cpu个数大于一,必须要是多核cpu。
  4。处于空闲状态或自旋状态的procs加上本协程小于gomaxprocs。
  5。当前p的待执行队列为空。Activespinningforsync。Mutex。go:linknamesyncruntimecanSpinsync。runtimecanSpingo:nosplitfuncsyncruntimecanSpin(iint)bool{ifiactivespinncpu1gomaxprocsint32(sched。npidlesched。nmspinning)1{returnfalse}ifp:getg()。m。p。ptr();!runqempty(p){returnfalse}returntrue}slowLock总结在自旋的过程中会尝试设置mutexWoken来通知解锁操作不去唤醒其他已经休眠的协程,在自旋模式下,当前的协程就能更快的获取到锁。自旋完成之后,就会去计算当前的锁的状态,如果当前处在饥饿模式下则不会去请求锁。状态计算完成之后就会尝试使用CAS操作获取锁,如果获取成功就会直接退出循环。如果没有获取到就调用runtimeSemacquireMutex(m。sema,queueLifo,1)方法休眠当前协程。协程被唤醒之后会先判断当前是否处在饥饿状态。1。如果处在饥饿状态就会获得互斥锁,如果等待队列中只存在当前协程或者当前协程不饥饿(如果当前协程超过等待超过1ms还没有获取到锁就会饥饿),会将互斥锁切换成正常模式。2。如果不是饥饿模式就会设置唤醒和饥饿标记、重置自旋次数并重新执行获取锁的循环。runtimeSemacquireMutex实现细节Semacquirewaitsuntils0andthenatomicallydecrementsit。Itisintendedasasimplesleepprimitiveforusebythesynchronizationlibraryandshouldnotbeuseddirectly。Semacquire等待直到s0并且自动减少s值,这是专门为同步库设计的简单的睡眠原语,不应该被直接使用。funcruntimeSemacquire(suint32)SemacquireMutexislikeSemacquire,butforprofilingcontendedMutexes。Iflifoistrue,queuewaiterattheheadofwaitqueue。skipframesisthenumberofframestoomitduringtracing,countingfromruntimeSemacquireMutexscaller。SemacquireMutex跟Semacquire类似,不过是为了互斥锁特殊优化的,如果lifo为true直接将当前goroutine放在阻塞队列首位。funcruntimeSemacquireMutex(suint32,lifobool,skipframesint)加锁流程图
  解锁Unlockunlocksm。ItisaruntimeerrorifmisnotlockedonentrytoUnlock。AlockedMutexisnotassociatedwithaparticulargoroutine。ItisallowedforonegoroutinetolockaMutexandthenarrangeforanothergoroutinetounlockit。解锁一个没有锁定的互斥量会报运行时错误。一个加锁的Mutex并不和特定的协程绑定,允许一个协程对mutex加锁然后安排另一个协程解锁。func(mMutex)Unlock(){ifrace。Enabled{m。staterace。Release(unsafe。Pointer(m))}Fastpath:droplockbit。new:atomic。AddInt32(m。state,mutexLocked)ifnew!0{如果new等于零表示没有其他任何需要通知的协程。直接返回Outlinedslowpathtoallowinliningthefastpath。TohideunlockSlowduringtracingweskiponeextraframewhentracingGoUnblock。m。unlockSlow(new)}}func(mMutex)unlockSlow(newint32){解锁一个未加锁的Mutex会报错if(newmutexLocked)mutexLocked0{throw(sync:unlockofunlockedmutex)}锁释放后的后续处理工作:1。正常模式下唤醒一个等待的协程竞争锁。2。饥饿模式下唤醒等待队列首位的协程获取锁。ifnewmutexStarving0{old:newfor{Iftherearenowaitersoragoroutinehasalreadybeenwokenorgrabbedthelock,noneedtowakeanyone。Instarvationmodeownershipisdirectlyhandedofffromunlockinggoroutinetothenextwaiter。Wearenotpartofthischain,sincewedidnotobservemutexStarvingwhenweunlockedthemutexabove。Sogetofftheway。如果没有等待的协程或者已经有被唤醒的协程或者锁已经被重新抢到或者锁已经变成饥饿模式,则直接退出。ifoldmutexWaiterShift0old(mutexLockedmutexWokenmutexStarving)!0{return}Grabtherighttowakesomeone。将等待着减一并且设置唤醒位new(old1mutexWaiterShift)mutexWokenifatomic。CompareAndSwapInt32(m。state,old,new){唤醒等待goroutineruntimeSemrelease(m。sema,false,1)return}oldm。state}}else{Starvingmode:handoffmutexownershiptothenextwaiter,andyieldourtimeslicesothatthenextwaitercanstarttorunimmediately。Note:mutexLockedisnotset,thewaiterwillsetitafterwakeup。ButmutexisstillconsideredlockedifmutexStarvingisset,sonewcominggoroutineswontacquireit。饥饿模式下直接唤醒队首的协程并且让出时间片使其可以立即运行注意:此时mutexLocked还未设置,被唤醒的协程在被唤醒后会进行设置,但是在饥饿模式下Mutex会仍然被认为在加锁状态,因此新来的goroutine不会获取到锁runtimeSemrelease(m。sema,true,1)}}
  解锁主要做三部分工作:
  尝试快速解锁。正常工作模式下尝试唤醒一个等待中的协程去竞争锁(如果已经有别的协程抢到了锁或者已经存在唤醒中的协程或者没有需要等待的协程或者锁被设置成了饥饿模式,则不再进行操作)。饥饿工作模式下直接唤醒第一个等待的协程把锁交给它(饥饿模式下一定有等待加锁的协程)。runtimeSemrelease详解SemreleaseatomicallyincrementssandnotifiesawaitinggoroutineifoneisblockedinSemacquire。Itisintendedasasimplewakeupprimitiveforusebythesynchronizationlibraryandshouldnotbeuseddirectly。Ifhandoffistrue,passcountdirectlytothefirstwaiter。skipframesisthenumberofframestoomitduringtracing,countingfromruntimeSemreleasescaller。Semrelease自动增加s的值并且唤醒一个阻塞的goroutine。如果handoff等于true,直接将count传递给第一个等待者!funcruntimeSemrelease(suint32,handoffbool,skipframesint)饥饿模式解锁
  解锁后state有mutexStarving标识,无mutexLocked标识。被唤醒的协程会设置mutexLocked状态并将等待的协程数减1。需要注意的是并不会马上清除mutexStarving标识而是一直在饥饿模式下工作,直到遇到第一个符合条件的协程才会清除mutexStarving标识。
  在饥饿模式下,如果有新的协程来请求抢占锁,mutex会被认为还处于锁状态,所以新来的协程不会进行抢占锁操作。解锁流程图:
  总结
  本文通过对Mutex的设计思想的深入分析和对源码实现的探索全面的讲解了GolangMutex互斥锁的设计。
  总结得出Mutex通过正常模式和饥饿模式两种模式来平衡锁的效率和公平性。
  希望对阅读这篇文章的你对锁的理解有所帮助,如果文章中存在错误之处欢迎指出,我们共同进步。推荐阅读
  Golang实用教程:协程的应用技巧
  Golang内存模型

第十二届服博会开幕武汉台企台胞共话服务外包发展新机遇第十二届中国国际服务外包交易博览会现场(图片来源武汉市台办)中国台湾网3月23日讯21日上午,第十二届中国国际服务外包交易博览会在武汉开幕。本届服博会围绕数字新机遇,外包新未来主题为最不发达国家创造一个繁荣的未来第五次联合国最不发达国家问题会议日前在卡塔尔首都多哈举行。会议通过了多哈政治宣言,强调采取实际行动落实多哈行动纲领,促进最不发达国家经济转型和释放发展潜力,推动实现联合国2030年玉面飞侠郭士强,曾是国家队最年轻主帅,目前在广州近况如何郭士强给人的印象是皮肤白皙,面色温和,言语不多,喜怒不形于色。他是辽宁篮球的代表人物,在球员时期郭士强多次获得助攻王称号,在辽宁做主教练时期,郭士强曾获得过CBA总冠军,还一度成为有的国家有的国家,暴虐无道发动战争,唯恐天下不乱。有的国家,精神分裂单边制裁,见不得别国比自己好。有的国家,卑鄙龌龊狼狈为奸,企图打破别国梦想。有的国家,看似活蹦乱跳四处张狂,其实已是秋后江西日报新时代新征程新伟业专栏报道萍乡坚定不移推进工业强市来源江西日报江西新闻客户端原标题精准施策久久为功萍乡坚定不移推进工业强市江西新闻客户端萍乡讯(江西日报全媒体记者尹晓军刘启红)日前,萍乡市今年主要工业经济指标出炉工业总量力争达到全郑永年改革开放创新是实现高质量发展的三大法宝郑永年,CCG学术专家委员会主任香港中文大学(深圳)全球与当代中国高等研究院院长。2023年3月17日,香港中文大学(深圳)教授广州粤港澳大湾区研究院理事长郑永年在广州首届百家新锐趣头条退市!曾因违法广告被央视315点名,盘点趣头条商业版图据美国证券交易委员会3月20日发布文件显示,趣头条于3月14日收到纳斯达克关于将股票退市的决定函。趣头条方面表示不会就该退市决定提出上诉,这意味着其股票将从当地时间3月23日起暂停法院对乐视控股所持股权询价评估天眼查App显示,3月23日,乐视控股(北京)有限公司新增询价评估信息,标的物为北京宏城鑫泰置业有限公司100股权,选定评估机构为上海立信资产评估有限公司北京分公司中瑞世联资产评估狂飙17高启强设局把自己送进监狱,与安欣飙车,借警察清除异己李宏伟吸毒被抓,安欣想从他嘴里套出谁是李青同伙,联合杨建故意吓唬他李宏伟被吓得不轻,一五一十供出同伙视频加载中随后李响带人到她家搜查,在墙缝中找到了案发当天的录像带,锁定了同伙身份三家国有大行密集发行二级资本债提前用完监管单次批复限额国有大行二级资本债密集发行。3月22日,农业银行发布的信息显示,该行于3月21日在银行间市场成功簿记发行700亿元二级资本债,其中55年期450亿元,发行利率3。49105年期25三国52鲁肃给刘备指明方向,劝孙权别做第三只鸟,那里边太挤了当刘备陷入了人生低谷,却让诸葛亮着急了,本是出来跟着刘备一起打江山,结果一战下来,让刘备开启了,怀疑人生的模式。就在这时小兵来报,说江东使者鲁肃前在求见,这让诸葛亮瞬间看到了转机。
长城美食延庆重关险隘深处,寻觅朴实至味许多游客来到北京,都要去长城游览一番,毕竟不到长城非好汉的诗句,让不少人为之向往。而提起长城,大部分人最先想到的就是位于北京延庆区的八达岭长城。这段位于北京延庆区军都山关沟古道北口两会新期待丨住陕全国政协委员祁志峰履职以来调研足迹遍布祖国各地脚踏实地让提案开花结果编者按新起点,新征程。2023年全国两会召开在即,新一届在陕全国人大代表住陕全国政协委员们有哪些履职感受?将带来哪些真知灼见?对未来发展有哪些憧憬和展望?即日起,学习强国陕西学习平这6句朴实的话,送给想要变得更好的你1hr哪怕进步一点点也总比什么都不做好生活中,有些人总因为嫌弃收获太少,而放弃努力。每天读5分钟的书写10分钟的复盘日记做20分钟的运动,或许这些微小的事不会立刻给你的人生带来巨大活着里这四句朴实的话,送给想要活得轻松的你世界上没有一条道路是重复的,也没有一个人生是能够替代的。有句话说人是为活着本身而活着,而不是为了活着之外的任何事物所活着。1992年,余华出版了一篇长篇小说活着。他是一个旁观者,他脚踏实地,筑造核电装备高端制造基地山东核电设备制造有限公司脚踏实地,筑造核电装备高端制造基地山东核电设备制造有限公司山东核电设备制造有限公司(以下简称国核设备)成立于2007年7月,是国家电力投资集团公司二级单位,是世界首家非能动压水堆核中国女篮王丽丽只有脚踏实地打球,才能不辜负信任支持和赞美人生的成长过程中,老一辈的梦想,会让年轻一辈去实现,会鼓励他们,帮助他们,让他们出去闯荡和眺望世界。一个走向更大舞台的人,他也一定会变得更强,人生就是一直挑战,一直追求更大舞台,挑年轻人难以理解的人生态度失意淡然,得意泰然导语常言道为什么有人会尊重你?他(她)人是尊重你的人品,还是你的地位和学识有人喜欢你,你要弄清楚一些,他(她)是喜欢你的才华,还是你的财富有人佩服你,你得搞清楚,他(她)是佩服你的茶能醉人何须酒!遵义采茶尝鲜地图来啦,周末赶紧约采茶尝鲜地图点击图片可放大查看点击图片可放大查看北纬27北纬27,一个看似平常的纬度。遵义,这条纬度线上一个看似平常的城市,但这样的平凡交织在一起,却造就了地球纬度生物多样性最为丰遵义境内的古酒提要1838年道光版遵义府志1914年版续遵义府志和1948年版遵义新志分别记载了遵义境内的古酒有茅台酒(茅台烧)咂酒(芦酒炉酒筒酒钩藤酒钓竿酒竿儿酒等)鸭溪雷泉等,另外有甜酒(醪腾讯元老魏震赚够钱后果断辞职,携妻儿退隐山林,每天种地喝茶阅读此文前,诚邀您点击一下关注,既方便您进行讨论与分享,又给您带来不一样的参与感,感谢您的支持。引言魏震作为曾经腾讯的元老淘米网络的创始人,2017年,他突然从前景一片大好的公司卸TA吕迪格与安德玛签下了一份为期五年的合同直播吧3月20日讯据TheAthletic报道,吕迪格已经与安德玛完成签约。据悉,吕迪格在2月皇马52战胜利物浦的欧冠比赛中首次穿上了安德玛的战靴。吕迪格与耐克的合同在2022年结
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网