Golang 讀寫鎖RWMutex 互斥鎖Mutex 源碼詳解

前言

Golang中有兩種類型的鎖病梢,Mutex (互斥鎖)和RWMutex(讀寫鎖)對(duì)于這兩種鎖的使用這里就不多說(shuō)了示括,本文主要側(cè)重于從源碼的角度分析這兩種鎖的具體實(shí)現(xiàn)股淡。

引子問(wèn)題

我一般喜歡帶著問(wèn)題去看源碼声邦。那么對(duì)于讀寫鎖编丘,你是否有這樣的問(wèn)題挽牢,為什么可以有多個(gè)讀鎖谱煤?有沒(méi)有可能出現(xiàn)有協(xié)程一直無(wú)法獲取到寫鎖的情況?帶著你的疑問(wèn)來(lái)往下看看禽拔,具體這個(gè)鎖是如何實(shí)現(xiàn)的刘离。

如果你自己想看,我給出閱讀的一個(gè)思路睹栖,可以先看讀寫鎖寥闪,因?yàn)樽x寫鎖的實(shí)現(xiàn)依賴于互斥鎖,并且讀寫鎖比較簡(jiǎn)單一些磨淌,然后整理思路之后再去想一下實(shí)際的應(yīng)用場(chǎng)景疲憋,然后再去看互斥鎖。

下面我就會(huì)按照這個(gè)思路一步步往下走梁只。

基礎(chǔ)知識(shí)點(diǎn)

  • 知識(shí)點(diǎn)1:信號(hào)量
    信號(hào)量是 Edsger Dijkstra 發(fā)明的數(shù)據(jù)結(jié)構(gòu)(沒(méi)錯(cuò)就是那個(gè)最短路徑算法那個(gè)牛人)缚柳,在解決多種同步問(wèn)題時(shí)很有用埃脏。其本質(zhì)是一個(gè)整數(shù),并關(guān)聯(lián)兩個(gè)操作:

申請(qǐng)acquire(也稱為 wait秋忙、decrement 或 P 操作)
釋放release(也稱 signal彩掐、increment 或 V 操作)

acquire操作將信號(hào)量減 1,如果結(jié)果值為負(fù)則線程阻塞灰追,且直到其他線程進(jìn)行了信號(hào)量累加為正數(shù)才能恢復(fù)堵幽。如結(jié)果為正數(shù),線程則繼續(xù)執(zhí)行弹澎。
release操作將信號(hào)量加 1朴下,如存在被阻塞的線程,此時(shí)他們中的一個(gè)線程將解除阻塞苦蒿。

  • 知識(shí)點(diǎn)2:鎖的定義

    image

    在goalng中如果實(shí)現(xiàn)了Lock和Unlock方法殴胧,那么它就可以被稱為鎖。

  • 知識(shí)點(diǎn)3:鎖的自旋:(詳見(jiàn)百度)

  • 知識(shí)點(diǎn)4:cas算法:(最好有所了解佩迟,不知道問(wèn)題也不大)

讀寫鎖RWMutex

首先我們來(lái)看看RWMutex大體結(jié)構(gòu)

image

看到結(jié)構(gòu)發(fā)現(xiàn)讀寫鎖內(nèi)部包含了一個(gè)w Mutex互斥鎖
注釋也很明確团滥,這個(gè)鎖的目的就是控制多個(gè)寫入操作的并發(fā)執(zhí)行
writerSem是寫入操作的信號(hào)量
readerSem是讀操作的信號(hào)量
readerCount是當(dāng)前讀操作的個(gè)數(shù)
readerWait當(dāng)前寫入操作需要等待讀操作解鎖的個(gè)數(shù)
這幾個(gè)現(xiàn)在看不懂沒(méi)關(guān)系,后面等用到了你再回來(lái)看就好了报强。

然后我們看看方法

image

一共有5個(gè)方法灸姊,看起來(lái)就不復(fù)雜,我們一個(gè)個(gè)來(lái)看秉溉。

image

這個(gè)最簡(jiǎn)單力惯,就是返回一個(gè)locker對(duì)象沒(méi)啥好說(shuō)的

問(wèn)題的關(guān)鍵就在于鎖和解鎖的幾個(gè)方法,因?yàn)槲乙呀?jīng)看過(guò)坚嗜,所以推薦這幾個(gè)方法的閱讀順序是RLock Lock RUnlock Unlock

RLock(獲取讀鎖)

image

先不看競(jìng)態(tài)檢測(cè)的部分夯膀,先重點(diǎn)看紅色框中的部分
可以看到,其實(shí)很簡(jiǎn)單苍蔬,每當(dāng)有協(xié)程需要獲取讀鎖的時(shí)候诱建,就將readerCount + 1
但是需要注意的是,這里有一個(gè)條件碟绑,當(dāng)readerCount + 1之后的值 < 0的時(shí)候俺猿,那么將會(huì)調(diào)用runtime_Semacquire方法
image

這個(gè)方法是一個(gè)runtime的方法,會(huì)一直等待傳入的s出現(xiàn)>0的時(shí)候
然后我們可以記得格仲,這里有這樣一個(gè)情況押袍,當(dāng)出先readerCount + 1為負(fù)數(shù)的情況那么就會(huì)被等待,看注釋我們可以猜到凯肋,是當(dāng)有寫入操作出現(xiàn)的時(shí)候谊惭,那么讀操作就會(huì)被等待。

Lock(獲取寫鎖)

image

寫鎖稍微復(fù)雜一些,但是樣子也差不多圈盔,我們還是先來(lái)看紅色框中的部分豹芯。
首先操作最前面說(shuō)的互斥鎖,目的就是處理多個(gè)寫鎖并發(fā)的情況驱敲,因?yàn)槲覀冎缹戞i只有一把铁蹈。這里不需要深入互斥鎖,只需要知道众眨,互斥鎖只有一個(gè)人能拿到握牧,所以寫鎖只有一個(gè)人能拿到。

然后重點(diǎn)來(lái)了娩梨,這里的這個(gè)操作細(xì)細(xì)體會(huì)一下沿腰,atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders)
是將當(dāng)前的readerCount減去一個(gè)非常大的值rwmutexMaxReaders為1 << 30
大概是1073741823這么大吧

所以我們可以從源碼中看出,readerCount由于每有一個(gè)協(xié)程獲取讀鎖就+1姚建,一直都是正數(shù)矫俺,而當(dāng)有寫鎖過(guò)來(lái)的時(shí)候吱殉,就瞬間減為很大的負(fù)數(shù)掸冤。
然后做完上面的操作以后的r其實(shí)就是原來(lái)的readerCount。
后面進(jìn)行判斷友雳,如果原來(lái)的readerCount不為0(原來(lái)有協(xié)程已經(jīng)獲取到了讀鎖)并且將readerWait加上readerCount(表示需要等待readerCount這么多個(gè)讀鎖進(jìn)行解鎖)稿湿,如果滿足上述條件證明原來(lái)有讀鎖,所以暫時(shí)沒(méi)有辦法獲取到寫鎖押赊,所以調(diào)用runtime_Semacquire進(jìn)行等待饺藤,等待的信號(hào)量為writerSem

RUnlock(釋放讀鎖)

image

如果是我們來(lái)寫的話,可能就是將之前+1的readerCount流礁,-1就完事了涕俗,但是其實(shí)還有一些操作需要注意。
如果-1之后+1==0是啥情況神帅?沒(méi)錯(cuò)就是我們常見(jiàn)的再姑,新手程序員,沒(méi)有獲取讀鎖就想去釋放讀鎖找御,于是異常了元镀。當(dāng)然+1之后剛好是rwmutexMaxReaders,就證獲取了寫鎖而去釋放了讀鎖霎桅,導(dǎo)致異常栖疑。
除去異常情況,剩下的就是r還是<0的情況滔驶,那么證明確實(shí)有協(xié)程正在想要獲取寫鎖遇革,那么就需要操作我們前面看到的readerWait,當(dāng)readerWait減到0的時(shí)候就證明沒(méi)有人正在持有寫鎖了,就通過(guò)信號(hào)量writerSem的變化告知?jiǎng)偛诺却膮f(xié)程(想要獲取寫鎖的協(xié)程):你可以進(jìn)行獲取了萝快。

到這里你可以把思路大致串起來(lái)了比原,然后懂了再往下看。

Unlock(釋放寫鎖)

image

寫鎖釋放需要恢復(fù)readerCount杠巡,還記得上鎖的時(shí)候減了一個(gè)很大的數(shù)量窘,這個(gè)時(shí)候要加回來(lái)了。
當(dāng)然加完之后如果>=rwmutexMaxReaders本身氢拥,那么還是新手程序員的問(wèn)題蚌铜,當(dāng)沒(méi)有獲取寫鎖的時(shí)候就開始想著釋放寫鎖了。
然后for循環(huán)就是為了通知所有在我們RLock方法中看到的嫩海,當(dāng)有因?yàn)槌钟袑戞i所以等待的那些協(xié)程冬殃,通過(guò)信號(hào)量readerSem告訴他們可以動(dòng)了。
最后別忘記還有一個(gè)互斥鎖需要釋放叁怪,讓別的協(xié)程也可以開始搶寫鎖了审葬。

至此,讀寫鎖的分析基本上告一段落了奕谭。
針對(duì)于其中關(guān)于競(jìng)態(tài)分析的代碼涣觉,有興趣的小伙伴可以去了解一下。

互斥鎖Mutex

互斥鎖比讀寫鎖復(fù)雜血柳,但是好在golang給的注釋很詳細(xì)官册,所以也不困難(注釋真的很重要)。
我們先來(lái)看看里面的一段注釋:

image

很長(zhǎng)的一段英文难捌,我用英語(yǔ)四級(jí)的翻譯能力給你翻譯一下膝宁,可以將就看看,如果可以建議你仔細(xì)看英文看懂它根吁,因?yàn)檫@對(duì)于后面的源碼閱讀非常重要员淫。
///
這個(gè)互斥鎖是公平鎖

互斥鎖有兩種操作模式:正常模式和饑餓模式。
在正常模式下等待獲取鎖的goroutine會(huì)以一個(gè)先進(jìn)先出的方式進(jìn)行排隊(duì)击敌,但是被喚醒的等待者并不能代表它已經(jīng)擁有了這個(gè)mutex鎖介返,它需要與新到達(dá)的goroutine爭(zhēng)奪mutex鎖。新來(lái)的goroutine有一個(gè)優(yōu)勢(shì) —— 他們已經(jīng)在CPU上運(yùn)行了并且他們愚争,所以搶到的可能性大一些映皆,所以一個(gè)被喚醒的等待者有很大可能搶不過(guò)。在這樣的情況下轰枝,被喚醒的等待者在隊(duì)列的頭部捅彻。如果一個(gè)等待者搶鎖超過(guò)1ms失敗了,就會(huì)切換為饑餓模式鞍陨。

在饑餓模式下步淹,mutex鎖會(huì)直接由解鎖的goroutine交給隊(duì)列頭部的等待者从隆。
新來(lái)的goroutine不能嘗試去獲取鎖,即使可能根本就沒(méi)goroutine在持有鎖缭裆,并且不能嘗試自旋键闺。取而代之的是他們只能排到隊(duì)伍尾巴上乖乖等著。

如果一個(gè)等待者獲取到了鎖澈驼,并且遇到了下面兩種情況之一辛燥,就恢復(fù)成正常工作模式。
情況1:它是最后一個(gè)隊(duì)列中的等待者缝其。
情況2:它等待的時(shí)間小于1ms

正常模式下挎塌,即使有很多阻塞的等待者,有更好的表現(xiàn)内边,因?yàn)橐惠喣芏啻潍@得鎖的機(jī)會(huì)榴都。饑餓模式是為了避免那些一直在隊(duì)尾的倒霉蛋。
///

我的話簡(jiǎn)單總結(jié)就是漠其,互斥鎖有兩種工作模式嘴高,競(jìng)爭(zhēng)模式和隊(duì)列模式,競(jìng)爭(zhēng)就是大家一起搶和屎,隊(duì)列就是老老實(shí)實(shí)排隊(duì)拴驮,這兩種工作模式會(huì)通過(guò)一些情況進(jìn)行切換。

首先還是來(lái)看看大體結(jié)構(gòu)

image

可以看到眶俩,相對(duì)讀寫鎖莹汤,結(jié)構(gòu)上面很簡(jiǎn)單快鱼,只有兩個(gè)值颠印,但是千萬(wàn)不要小瞧它,減少了字段就增加了理解難度抹竹。
state:將一個(gè)32位整數(shù)拆分為:
當(dāng)前阻塞的goroutine數(shù)(29位)
饑餓狀態(tài)(1位线罕,0為正常模式;1為饑餓模式)
喚醒狀態(tài)(1位窃判,0未喚醒钞楼;1已喚醒)
鎖狀態(tài)(1位,0可用袄琳;1占用)

sema:信號(hào)量

image

方法也很簡(jiǎn)單询件,就是Lock和Unlock兩個(gè)方法,一個(gè)上鎖唆樊,一個(gè)解鎖宛琅,沒(méi)啥好說(shuō)的。

一個(gè)方法

我們先來(lái)看一個(gè)的要用到的方法

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
這個(gè)函數(shù)逗旁,會(huì)先判斷參數(shù)addr指向的被操作值與參數(shù)old的值是否相等嘿辟,如果相等會(huì)將參數(shù)new替換參數(shù)addr所指向的值,不然的話就啥也不做。
需要特別說(shuō)明的是英古,這個(gè)方法并不會(huì)阻塞昙读。

幾個(gè)常量

這是定義的幾個(gè)常量,我們?cè)谝婚_始的注釋周圍可以看到蛮浑,后面需要用到某残,暫時(shí)記住它們的初始值就好陵吸。

mutexLocked = 1 << iota // 1左移0位,是1壮虫,二進(jìn)制是1澳厢,(1表示已經(jīng)上鎖)
mutexWoken // 1左移1位,是2剩拢,二進(jìn)制是10
mutexStarving // 1左移2位饶唤,是4募狂,二進(jìn)制是100
mutexWaiterShift = iota // 就是3, 二進(jìn)制是11

starvationThresholdNs = 1e6 // 這個(gè)就是我們一開始在注釋里面看到的1ms祸穷,一定超過(guò)這個(gè)門限值就會(huì)更換模式

Lock獲取鎖

因?yàn)長(zhǎng)ock方法比較長(zhǎng)雷滚,所以我切分一段段看,需要完整的請(qǐng)自己翻看源碼呆万。要注意的一點(diǎn)是车份,一定要時(shí)刻記住,Lock方法是做什么的逃顶,很簡(jiǎn)單,就是要搶鎖霸褒∮看不懂的時(shí)候想想這個(gè)目標(biāo)抖誉。

image

第一步,判斷state狀態(tài)是否為0旁理,如果為0孽文,證明沒(méi)有協(xié)程持有鎖夺艰,那么就很簡(jiǎn)單了,直接獲取到鎖减牺,將mutexLocked(為1)賦值到state就可以了拔疚。

看后面的方法時(shí)愕贡,告訴需要告訴你們一個(gè)小技巧固以,當(dāng)遇到這種位操作很多的情況嘱巾,有兩個(gè)方法挺好用旬昭,對(duì)于你看源碼會(huì)有幫助:
第一個(gè)是將所有定值先計(jì)算,然后判斷非定值的情況遍略;
第二個(gè)是將所有的計(jì)算寫下來(lái),自己用筆去計(jì)算下愈,不要執(zhí)著于打字势似。

然后我們以下面這個(gè)段舉例:


image

首先僧著,看注釋應(yīng)該能明白這一段大致意思是盹愚,如果不是饑餓模式,就會(huì)進(jìn)行自旋操作霞篡,然后不斷循環(huán)朗兵。

然后根據(jù)上面的技巧顶滩,old&(mutexLocked|mutexStarving) == mutexLocked
(下面均為二進(jìn)制)
mutexLocked = 1
mutexStarving = 11
mutexLocked = 1
這三個(gè)是定值礁鲁,所以我們?nèi)菀椎玫剑瑵M足情況的結(jié)果為冗美,當(dāng)old為xxxx0xx(二進(jìn)制第三位為0)等式成立析二。
也就是我們一開始說(shuō)的叶摄,state的第三位是表示這個(gè)鎖當(dāng)前的模式蛤吓,0為正常模式,1為饑餓模式锅棕。

那么第一個(gè)if就表示,如果當(dāng)前模式為正常模式顾瞻,且可以自旋朋其,就進(jìn)入if條件內(nèi)部脆炎。
if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&

同樣的分析秒裕,awoke表示是否喚醒,old&mutexWoken是取第二位喇潘,0表示當(dāng)前協(xié)程未被喚醒梭稚,old>>mutexWaiterShift表示右移3位弧烤,也就是前29位暇昂,不為0證明有協(xié)程在等待,并且嘗試去對(duì)比當(dāng)前m.state與取出時(shí)的old狀態(tài)从铲,嘗試去喚醒自己澄暮。然后自旋,并且增加自旋次數(shù)表示iter吉嫩,然后重新賦值old嗅定。再循環(huán)下一次渠退。

(你自己理一理碎乃,確實(shí)有點(diǎn)繞,仔細(xì)想想就想通了就對(duì)了恰梢。)

以上是使用自旋的情況梗掰,就是canSpin的及穗。

image

然后進(jìn)行判斷old&mutexStarving == 0就是第三位為0的情況埂陆,還是所說(shuō)的正常模式焚虱。new就馬上拿到鎖了,new |= mutexLocked躏率,表示或1谍咆,就是第一位無(wú)論是啥都賦值為1

old&(mutexLocked|mutexStarving)摹察,也就是old & 0101
必須當(dāng)old的1和3兩個(gè)位置為1的時(shí)候才是true供嚎,也就是說(shuō)當(dāng)前處于饑餓模式,并且鎖已經(jīng)被占用的情況逼争,那么就需要排隊(duì)去誓焦。
排隊(duì)也很精妙着帽,new += 1 << mutexWaiterShift
這邊注意是先計(jì)算1 << mutexWaiterShift也就是將new的前29位+1,就是表示有一個(gè)協(xié)程在等待了观话。

好了到這里你的位操作應(yīng)該就習(xí)慣的差不多了越平,之后我就直接說(shuō)結(jié)論秦叛,不仔細(xì)的幫你01表示了,你已經(jīng)長(zhǎng)大了尼变,要學(xué)會(huì)自己動(dòng)手了嫌术。

如果當(dāng)前已經(jīng)標(biāo)記為饑餓模式牌借,并且沒(méi)有鎖住膨报,那么設(shè)置new為饑餓模式
if starving && old&mutexLocked != 0 {
new |= mutexStarving
}

如果喚醒现柠,需要在兩種情況下重設(shè)標(biāo)志
if awoke {
如果喚醒標(biāo)志為與awoke不相協(xié)調(diào)就panic
if new&mutexWoken == 0 {
throw("sync: inconsistent mutex state")
}
設(shè)置喚醒狀態(tài)位0,被喚醒
new &^= mutexWoken
}

image

如果獲取鎖成功

old&(mutexLocked|mutexStarving) == 0成立表示已經(jīng)獲取鎖够吩,就直接退出CAS

中間這一段我就不多解釋了,就是最前面注釋說(shuō)的强法,滿足什么條件轉(zhuǎn)換什么模式饮怯,不多說(shuō)了蓖墅。然后從隊(duì)列中,也就是前29位-1于樟。
需要注意其中有一個(gè)runtime_SemacquireMutex和之前看的的runtime_Semacquire是一個(gè)意思,只是多了一個(gè)參數(shù)靶橱。


image

這個(gè)就是這個(gè)方法的注釋传黄《涌埽可以看到佳遣,就是多了個(gè)隊(duì)列去排隊(duì)。

image

如果獲取鎖失敗,old刷新狀態(tài)再次循環(huán)诵盼,繼續(xù)cas

UnLock釋放鎖

image

Unlock就相對(duì)簡(jiǎn)單一些风宁,競(jìng)態(tài)分析不看戒财。
其實(shí)我們自己想也能想到固翰,unlock就是將標(biāo)識(shí)位改回來(lái)嘛。
然后因?yàn)槲覀円呀?jīng)看過(guò)讀寫鎖了疗琉,也是同樣的道理盈简,如果沒(méi)有上鎖就直接解鎖,那肯定報(bào)錯(cuò)嘛香浩。

image

然后如果是正常模式邻吭,如果沒(méi)有等待的goroutine或goroutine已經(jīng)解鎖完成的情況就直接返回了囱晴。如果有等待的goroutine那就通過(guò)信號(hào)量去喚醒runtime_Semrelease(注意這里是false)畸写,同時(shí)操作一下隊(duì)列-1

image

如果是饑餓模式就直接喚醒(注意這里是true)枯芬,反正有隊(duì)列嘛采郎。

互斥鎖總結(jié)

其實(shí)話說(shuō)回來(lái)尉剩,我們其實(shí)看起來(lái)也簡(jiǎn)單理茎,沒(méi)有沖突的情況下,能拿就拿唄朗鸠,如果出現(xiàn)沖突了就嘗試自旋解決(自旋一般都能解決)如果解決不了就通過(guò)信號(hào)量解決烛占,同時(shí)如果正常模式就是我們說(shuō)的搶占式忆家,非公平德迹,如果是饑餓模式胳搞,就是我們說(shuō)的排隊(duì)称杨,公平姑原,防止有一些倒霉蛋一直搶不到锭汛。

整體總結(jié)一下阴绢,看完源碼我們發(fā)現(xiàn)呻袭,其實(shí)鎖的設(shè)計(jì)并不復(fù)雜左电,主要設(shè)計(jì)我們要學(xué)到cas和處理讀寫狀態(tài)的信號(hào)量通知篓足,對(duì)于那些位操作闰蚕,能看懂没陡,學(xué)可能一時(shí)半會(huì)學(xué)不會(huì)盼玄,因?yàn)楹茈y在一開始就設(shè)計(jì)的那么巧妙,你也體會(huì)到了只用一個(gè)變量就維護(hù)了整個(gè)體系是一種藝術(shù)器仗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市剃斧,隨后出現(xiàn)的幾起案子悯衬,更是在濱河造成了極大的恐慌,老刑警劉巖炸渡,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚌堵,死亡現(xiàn)場(chǎng)離奇詭異吼畏,居然都是意外死亡嘁灯,警方通過(guò)查閱死者的電腦和手機(jī)丑婿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門秒旋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诀拭,“玉大人耕挨,你說(shuō)我怎么就攤上這事俗孝。” “怎么了插勤?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)盛卡。 經(jīng)常有香客問(wèn)我筑凫,道長(zhǎng),這世上最難降的妖魔是什么哩牍? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任膝昆,我火速辦了婚禮荚孵,結(jié)果婚禮上纬朝,老公的妹妹穿的比我還像新娘玄组。我一直安慰自己俄讹,他們只是感情好患膛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布踪蹬。 她就那樣靜靜地躺著,像睡著了一般臣咖。 火紅的嫁衣襯著肌膚如雪跃捣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天夺蛇,我揣著相機(jī)與錄音疚漆,去河邊找鬼。 笑死刁赦,一個(gè)胖子當(dāng)著我的面吹牛娶聘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播丸升,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼牺氨!你這毒婦竟也來(lái)了狡耻?” 一聲冷哼從身側(cè)響起墩剖,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夷狰,沒(méi)想到半個(gè)月后岭皂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孵淘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年蒲障,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘫证。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡揉阎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出背捌,到底是詐尸還是另有隱情毙籽,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布毡庆,位于F島的核電站坑赡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏么抗。R本人自食惡果不足惜毅否,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蝇刀。 院中可真熱鬧螟加,春花似錦、人聲如沸吞琐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)站粟。三九已至黍图,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奴烙,已是汗流浹背助被。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缸沃,地道東北人恰起。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像趾牧,于是被迫代替她去往敵國(guó)和親检盼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容