应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

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内存模型

销量大涨4倍的吉利新能源,下半年势头会更猛出品何玺排版叶媛8月18日,吉利汽车公布了2022年上半年财报。数据显示,吉利各项核心指标均显示出良好发展趋势。值得一提的是,2022上半年吉利新能源汽车销量大涨4倍,是……毫无悬念!本泽马当选欧足联年度最佳球员当地时间8月25日,2022年欧足联颁奖典礼在土耳其伊斯坦布尔举行。在欧足联年度最佳球员评选中,皇家马德里前锋本泽马毫无悬念地当选,首次获得这一荣誉。上赛季,本泽马各赛事……主机一哥再发大宏愿,已接近完成,有众多500强企业参与其中昨日是主机区知名主播超级小桀的30岁生日,都说三十而立,对于超级小桀,这个生日也是过得特别有意义,从全世界各地的水友手中寄过来的礼物,让桀哥昨日的直播完全成为了开箱大典,从中午……泰山官方因无名额为其进行注册,德尔加多租借至中甲昆山FC北京时间8月26日,中超球队山东泰山宣布,将队内的归化球员德尔加多租借到中甲球队昆山FC。目前,昆山FC在中甲排名第2,极有可能完成冲超,而德尔加多被外租的原因是,泰山外援名额……任正非刚说把活下来作主要纲领,慧聪网马上宣布不活了任正非说要对未来过于乐观的预期情绪要降下来,2023年甚至到2025年,一定要把活下来作为最主要的纲领活下来,有质量的活下来,每个业务都要去认真执行。同一天,慧聪网宣布停……通报通的是情感和温度九派时评九派新闻特约评论员赵代君情况通报是一种告知性的公文,用于告知重要情况,主要起着沟通情况的作用。通常我们看到的通报都是冷静的、格式化的。但是,今天(25日)看到的一篇……曼城在纽卡斯尔以33惊险拿到一分埃尔林哈兰德和贝尔纳多席尔瓦的快速进球帮助曼城从两球落后的情况下扳平比分,他们与纽卡斯尔联队在周日圣詹姆斯公园举行了一场轰轰烈烈的英超联赛交锋最终两队以33战平。本赛季双……三国志战略版三周年争霸赛!150VS150,一场航母区的盛宴关于50V50的玩法,刀子在之前的多篇文章中都有提到过,极有可能作为三周年的一个赛季模式。从今天的官微来看,基本已经坐实了,三周年霸业之巅龙虎争霸赛,采用的就是这种模式。……安徽人游安徽天长之旅,一定不能错过的民宿来到天长,不仅有美食美景,体验民宿也是旅行中的乐趣。在天长有这样三处民宿:长山民宿、观音湖民宿、汀溪民宿,屋旁侧绿树葱笼,青竹苍翠欲滴,让你住在风景里,全身心融入环境清幽的大自……全网首发12个职业,可转职觉醒,地下城传奇即将上线在9月这个天气晴朗、阵风清凉的秋天,工作学习的压力下难免不来款手游打发一下时间,疏松疏松心情。那么好直接进入正题,在现在的互联网上各种手游类似于传奇、仙侠、三国等逐步热火,尤其……恭喜!38岁女星宣宣成功产女,为生娃曾扎200多针,产检278月23日,据媒体报道,资深女星宣宣已经顺利产女,她透过个人社交账号宣布喜讯,网友们得悉后,纷纷留言恭喜。宣宣写道,最棒的礼物平安健康报到了,我真的当妈了,直到现在还是有……激光电视将要取代液晶电视?斥巨资踩坑得出结论是一波智商税最近正好家里电视坏了,在选购的过程中智能算法给我推送了激光电视,点进去一看,好家伙!激光电视直接糅合了各大高精尖的黑科技,将普通液晶电视完爆,评论区也是一片叫好,于是便入了坑。……
(体育)柯洁我曾站在山顶还想仰望星空新华社广州4月4日电柯洁:我曾站在山顶还想仰望星空新华社记者王浩明距离名人一步之遥。第33届中国围棋名人战3日进行挑战者决赛,柯洁九段执黑中盘战胜杨楷文九段,……全球身价最高的大码模特高177重180斤,没有赘肉,曲线凹凸2022年1月,33岁的阿什利格雷厄姆穿上一身绿色毛衣,拍摄杂志封面照。阿什利身价550万美元,是全球身价最高的大码模特。大码模特顾名思义就是代表着身材丰满肥硕一类女性的……上海半程万人马拉松4月16日开跑!最高奖金10000美元中新经纬3月9日电时隔两年,上海半马回来了!据上马微信公众号9日消息,2023浦发银行上海半程马拉松将于2023年4月16日(星期日)7:00举行。来源:上马微信公众号……女神们,三八节沈阳这些地方有优惠和免费活动!今天3月8日妇女节哦女神们愿你一路向阳永远充盈四季皆如春为迎接这一节日沈阳推出了很多精彩活动一起来看看吧2023沈阳……76岁老佛爷为皇马带来31个冠军奖杯,他是21世纪以来最成功头条创作挑战赛弗洛伦蒂诺,外号老佛爷,1947年3月8日出生于西班牙马德里,现任皇家马德里俱乐部主席。弗洛伦蒂诺应该是21世纪以来皇家马德里俱乐部最成功的主席,他成功地重……大批3A新作定档,首发RTX4060游戏本天选真香价9999凭借着超烧的冰箱,《原子之心》这款游戏近期可以说是彻底火出了圈,尤其是冰箱的配音相当火,不少人也正因为这个冰箱而正式入坑《原子之心》这款游戏。另外除开《原子之心》,2023年还……爱的形式关心责任心尊重和了解所有爱的形式共有的是:关心、责任心、尊重和了解。关心和关怀还包括爱情的另一方面,即责任心。今天人们常常把责任心理解为是义务,是外部强加的东西。但是责任心这个词的本来意义是……这些年有多少大项目落户拱墅?这个主城区留白最多的城区,要给自3月1日,拱墅区与京东集团签约。未来,京东将在拱墅区建成区域中心,入驻京东商城、京东物流、京东科技、京东健康等业务板块,后续还将落地京东超级体验店、京东智能开放创新研究院等重点……想念食品IPO如何讲好面和面粉的故事?今天吃了一碗面,叫好香合泥间意面。上述谐音梗段子来自想念食品股份有限公司(下称想念食品)的官方微博。近日,想念食品更新招股书,拟推进上交所主板IPO进程。想念食品拟发行不……云中城堡迎来200名幼儿!横琴伯牙幼儿园正式启用视频加载中。。。云中城堡迎来200名幼儿!横琴伯牙幼儿园正式启用3月6日,首都师范大学横琴伯牙幼儿园(下称伯牙幼儿园)正式开园,迎来了7个班级、近200名幼儿,其中……黄圣依参加巴黎时装周造型翻车,像穿睡衣出街,外媒生图被丑化明星扎堆参加巴黎时装周,造型有好有坏,就连章子怡造型也引发吐槽,最新消息,黄圣依参加巴黎时装周造型翻车,章子怡只是发型失败,黄圣依是服装失败。像穿睡衣出街,这样交叉绑带黑……止步八强,但请为中国足球的希望之光喝彩U20亚洲杯四分之一决赛,面对韩国队一整场的狂轰乱炸,率先进球的中国国青,在加时赛最终还是没能顶住对手的围攻,止步八强。虽然无法更进一步,但门将李昊多次的关键扑救,球员狂……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网