Go語言的GPM模型

1 調(diào)度模型

Linux操作系統(tǒng)中的資源調(diào)度是基于進程的蛉抓,同一進程中的線程共享這個進程中的所有資源诬垂,所以linux中的線程本質(zhì)上是一種輕量級進程量九,同樣被操作系統(tǒng)進行統(tǒng)一調(diào)度捆愁。而linux又將線程的實現(xiàn)分為兩種:

用戶級線程

  • 線程切換不需要轉(zhuǎn)換到內(nèi)核空間燕刻,節(jié)省了寶貴的內(nèi)核空間

  • 調(diào)度算法可以是進程專用饿凛,由用戶程序進行指定

  • 用戶級線程實現(xiàn)和操作系統(tǒng)無關(guān)

  • \color{red}{系統(tǒng)調(diào)用阻塞狞玛,同一進程中一個線程阻塞和整個進程都阻塞了}

  • \color{red}{一個線程只能在一個cpu上獲得執(zhí)行}

內(nèi)核級線程

  • 在多處理器上软驰,內(nèi)核可以調(diào)用同一進程中的多個線程同時工作

  • 如果一個進程中的一個線程阻塞了,其他線程仍然可以得到運行

  • 線程的切換代價太大心肪,需要進程進入到內(nèi)核態(tài)并且由內(nèi)核切換

1.1 一對一

該模型實現(xiàn)簡單锭亏,所有用戶線程由系統(tǒng)調(diào)用,導致上下文切換成本高硬鞍,用戶線程的增加會給操作系統(tǒng)內(nèi)核帶來巨大壓力

image

1.2 一對多

該模型雖然減少了內(nèi)核線程的數(shù)量慧瘤,但是用戶線程無法參與到系統(tǒng)的CPU調(diào)度中,且與固定的內(nèi)核線程綁定固该,對于用一個內(nèi)核線程下的用戶線程等于是串行锅减,一旦一個用戶線程阻塞,其他用戶線程會無法調(diào)度蹬音。

image

1.3 多對多

改模型中用戶線程和內(nèi)核線程非綁定上煤,應用程序和系統(tǒng)共同進行CPU資源的調(diào)度,解決了之前模型的缺點著淆,但是實現(xiàn)邏輯復雜劫狠。Golang使用的就是基于該模型的調(diào)度方案

image

2 GPM模型

image

(G)oroutine

每個 Goroutine 對應一個 G 結(jié)構(gòu)體,G 存儲 Goroutine 的運行堆棧永部、狀態(tài)以及任務(wù)函數(shù)独泞,可重用。G 并非執(zhí)行體苔埋,每個 G 需要綁定到 P 才能被調(diào)度執(zhí)行懦砂。

(P)rocessor

表示邏輯處理器, 對 G 來說组橄,P 相當于 CPU 核荞膘,G 只有綁定到 P(在 P 的 local runq 中)才能被調(diào)度。對 M 來說玉工,P 提供了相關(guān)的執(zhí)行環(huán)境(Context)羽资,如內(nèi)存分配狀態(tài)(mcache),任務(wù)隊列(G)等遵班,P 的數(shù)量決定了系統(tǒng)內(nèi)最大可并行的 G 的數(shù)量(前提:物理 CPU 核數(shù) >= P 的數(shù)量)屠升,P 的數(shù)量由用戶設(shè)置的 GOMAXPROCS 決定,但是不論 GOMAXPROCS 設(shè)置為多大狭郑,P 的數(shù)量最大為 256腹暖。

(M)achine

OS 線程抽象,代表著真正執(zhí)行計算的資源翰萨,在綁定有效的 P 后脏答,進入 schedule 循環(huán);而 schedule 循環(huán)的機制大致是從 Global 隊列、P 的 Local 隊列以及 wait 隊列中獲取 G以蕴,切換到 G 的執(zhí)行棧上并執(zhí)行 G 的函數(shù)糙麦,調(diào)用 goexit 做清理工作并回到 M,如此反復丛肮。M 并不保留 G 狀態(tài),這是 G 可以跨 M 調(diào)度的基礎(chǔ)魄缚,M 的數(shù)量是不定的宝与,由 Go Runtime 調(diào)整,為了防止創(chuàng)建過多 OS 線程導致系統(tǒng)調(diào)度不過來冶匹,目前默認最大限制為 10000 個习劫。

調(diào)度流程

  • 每個 P 維護一個 G 的本地隊列;

  • 當一個 G 被創(chuàng)建出來嚼隘,或者變?yōu)榭蓤?zhí)行狀態(tài)時诽里,就把他放到 P 的本地可執(zhí)行隊列中,如果滿了則放入Global飞蛹;

  • 當一個 G 在 M 里執(zhí)行結(jié)束后谤狡,P 會從隊列中把該 G 取出;如果此時 P 的隊列為空卧檐,即沒有其他 G 可以執(zhí)行墓懂, M 就隨機選擇另外一個 P,從其可執(zhí)行的 G 隊列中取走一半(work stealing)霉囚。

  • 為了避免G中出現(xiàn)系統(tǒng)調(diào)用捕仔、阻塞操作導致P&M被阻塞,Go中還引入了搶占式調(diào)度機制盈罐,每個函數(shù)的入口方法出都會注入一段代碼榜跌,用于判斷runtime此時是否可以進行搶占式調(diào)度,判定可以搶占后會由空閑M去搶占盅粪。

2.2 Goroutine

goroutines 意味著并行(或者可以以并行的方式部署)钓葫,coroutines 一般來說不是這樣的,期運行機制屬于協(xié)作式任務(wù)處理湾揽,coroutines在不需要使用 CPU 時瓤逼,會主動交出 CPU 使用權(quán),主要以并發(fā)方式部署库物。

image

goroutine用法如下:


//go 關(guān)鍵字放在方法調(diào)用前新建一個 goroutine 并執(zhí)行方法體

go GetThingDone(param1, param2);

//新建一個匿名方法并執(zhí)行

go func(param1, param2) {

}(val1, val2)

//直接新建一個 goroutine 并在 goroutine 中執(zhí)行代碼塊

go {

    //do someting...

}

代碼中的go關(guān)鍵字會被編譯器翻譯成newproc操作

G的創(chuàng)建

G結(jié)構(gòu)體會復用霸旗,對可復用的G管理類似于待運行的G管理,也有Local隊列(p.gfree)和Global隊列(sched.gfree)之分戚揭,獲取算法差不多诱告,優(yōu)先從p.gfree中獲取(無鎖操作),否則從sched.gfree中獲取并批量轉(zhuǎn)移一部分(有鎖操作)民晒,源代碼參考src/runtime/proc.go:gfget函數(shù)精居。

從Goroutine的角度來看锄禽,通過go func()創(chuàng)建時,會從當前閑置的G隊列取得可復用的G靴姿,如果沒有則通過malg新建一個G沃但,然后:

  • 如果不是gc協(xié)程或者從全局隊列挪進來的G,都會將G添加到當前P的runnext中佛吓,作為下一個執(zhí)行的G宵晚,然后runnext的舊值會被添加到runq隊列中去

  • 否則放到Local隊列runq中(無鎖)

  • 如果以上操作都失敗,則添加到Global隊列sched.runq中(有鎖操作维雇,因此也會順便將當P.runq中一半的G轉(zhuǎn)移到sched.runq)

2.3 Processor

image

為什么要有P淤刃?

如果沒有P:

  • 存在單一的全局 mutex(Sched.Lock)和集中狀態(tài)管理:

** mutex 需要保護所有與 goroutine 相關(guān)的操作(創(chuàng)建、完成吱型、重排等)逸贾,導致鎖競爭嚴重。

  • Goroutine 傳遞的問題:

-- goroutine(G)交接(G.nextg):工作者線程(M's)之間會經(jīng)常交接可運行的 goroutine津滞。

-- 上述可能會導致延遲增加和額外的開銷铝侵。每個 M 必須能夠執(zhí)行任何可運行的 G,特別是剛剛創(chuàng)建 G 的 M据沈。

  • 每個 M 都需要做內(nèi)存緩存(M.mcache):

-- 會導致資源消耗過大(每個 mcache 可以吸納到 2M 的內(nèi)存緩存和其他緩存)哟沫,數(shù)據(jù)局部性差。

  • 頻繁的線程阻塞/解阻塞:

-- 在存在 syscalls 的情況下锌介,線程經(jīng)常被阻塞和解阻塞嗜诀。這增加了很多額外的性能開銷。

有P之后:

  • 每個 P 有自己的本地隊列孔祸,大幅度的減輕了對全局隊列的直接依賴隆敢,所帶來的效果就是鎖競爭的減少。而 GM 模型的性能開銷大頭就是鎖競爭崔慧。

  • 每個 P 相對的平衡上拂蝎,在 GMP 模型中也實現(xiàn)了 Work Stealing 算法,如果 P 的本地隊列為空惶室,則會從全局隊列或其他 P 的本地隊列中竊取可運行的 G 來運行温自,減少空轉(zhuǎn),提高了資源利用率皇钞。

為什么不在M上實現(xiàn)

一般來講悼泌,M 的數(shù)量都會多于 P。像在 Go 中夹界,M 的數(shù)量默認是 10000馆里,P 的默認數(shù)量的 CPU 核數(shù)。另外由于 M 的屬性,也就是如果存在系統(tǒng)阻塞調(diào)用鸠踪,阻塞了 M丙者,又不夠用的情況下,M 會不斷增加营密。

M 不斷增加的話械媒,如果本地隊列掛載在 M 上,那就意味著本地隊列也會隨之增加卵贱。這顯然是不合理的滥沫,因為本地隊列的管理會變得復雜,且 Work Stealing 性能會大幅度下降键俱。

M 被系統(tǒng)調(diào)用阻塞后,我們是期望把他既有未執(zhí)行的任務(wù)分配給其他繼續(xù)運行的世分,而不是一阻塞就導致全部停止编振。

golang中的三種系統(tǒng)線程

  • 主線程:golang程序啟動加載的時候就運行在主線程上,代碼中由一個全局的m0表示

  • 運行sysmon的線程

  • 普通用戶線程臭埋,用來與p綁定踪央,運行g(shù)中的任務(wù)的線程

主線程和運行sysmon都是單實例,單獨一個線程瓢阴。而用戶線程會有很多事例畅蹂,他會根據(jù)調(diào)度器的需求新建,休眠和喚醒荣恐。

2.4 Machine

M和G液斜、P一樣都是復用的,一旦創(chuàng)建叠穆,就不會被銷毀少漆,M的狀態(tài)轉(zhuǎn)化是最簡單,除了M0之外硼被,都是通過newm()函數(shù)創(chuàng)建示损,然后通過mstart,進入調(diào)度循環(huán)嚷硫,之后可以通過stopm進入阻塞狀態(tài)检访,再通過startm喚醒,狀態(tài)圖如下:

image

為什么M數(shù)量會比P多仔掸?

因為M是golang中真正被操作系統(tǒng)管理并執(zhí)行邏輯的單元脆贵,G和P都是golang內(nèi)部的抽象概念

M承接了golang中所有的調(diào)度邏輯和協(xié)程邏輯,如果一個M由于syscall或其他原因被阻塞嘉汰,這時golang就需要啟動一個新的M去繼續(xù)進行其他G的調(diào)度丹禀,最壞的情況下每個G都會阻塞掉一個P。

runtime包提供了LockOSThread操作,會使得每個G與M一對一綁定

P實質(zhì)上映射的是計算機的物理CPU双泪,G實質(zhì)上是M在golang中的映射

2.5 啟動

通過 Entry point 的調(diào)試持搜,可看到真正的程序入口在 runtime 包中,不同的計算機架構(gòu)指向不同焙矛。例如:

MacOS 在 src/runtime/rt0_darwin_amd64.s

Linux 在 src/runtime/rt0_linux_amd64.s

rt0 代表 runtime0 的縮寫葫盼,指代運行時的創(chuàng)世Runtime

image

所有的goroutine的調(diào)用函數(shù)都是goexit,這樣當其執(zhí)行結(jié)束時村斟,可以返回到goexit中贫导,繼續(xù)調(diào)度循環(huán),goexit的地址保存在gobuf.pc中蟆盹,g0的棧是在主線程上分配的

2.6 調(diào)度

image

mstart中綁定好P之后孩灯,M擁有了可分配cache和執(zhí)行隊列,進入核心調(diào)度循環(huán)逾滥,核心調(diào)度從schedule函數(shù)開始峰档,調(diào)度完一次之后會引導重新執(zhí)行schedule,實現(xiàn)循環(huán)調(diào)度:

image

schedule方法的主要功能是盡可能給M找到可以運行的G寨昙,然后執(zhí)行:

  • 獲取當前調(diào)度的g讥巡,也就是g0,g0在執(zhí)行調(diào)度邏輯舔哪;

  • 如果當前GC需要停止整個世界(STW), 則調(diào)用gcstopm休眠當前的M欢顷;

  • 如果M擁有的P中指定了需要在安全點運行的函數(shù)(P.runSafePointFn), 則運行它;

  • 快速獲取待運行的G, 以下處理如果有一個獲取成功后面就不會繼續(xù)獲茸皆椤:

-- 如果當前GC正在標記階段, 則查找有沒有待運行的GC Worker, GC Worker也是一個G抬驴;

-- 為了公平起見, 每61次調(diào)度從全局運行隊列獲取一次G, (一直從本地獲取可能導致全局運行隊列中的G不被運行);

-- 從P的本地運行隊列中獲取G, 調(diào)用runqget函數(shù)外里。

  • 快速獲取失敗時, 調(diào)用findrunnable函數(shù)獲取待運行的G, 會阻塞到獲取成功為止:

-- 如果當前GC需要停止整個世界(STW), 則調(diào)用stopm休眠當前的M怎爵;

-- 如果M擁有的P中指定了需要在安全點運行的函數(shù)(P.runSafePointFn), 則運行它;

-- 如果有析構(gòu)器待運行則使用"運行析構(gòu)器的G"盅蝗;

-- 從P的本地運行隊列中獲取G, 調(diào)用runqget函數(shù)鳖链,如果獲取到就返回;

-- 從全局運行隊列獲取G, 調(diào)用globrunqget函數(shù), 需要上鎖墩莫,獲取到就返回芙委。;

-- 從網(wǎng)絡(luò)事件反應器獲取G, 函數(shù)netpoll會獲取哪些fd可讀可寫或已關(guān)閉, 然后返回等待fd相關(guān)事件的G狂秦;

-- 如果從local 和 global 都獲取不到G, 則執(zhí)行Work Stealing:

--- 調(diào)用runqsteal嘗試從其他P的本地運行隊列盜取一半的G灌侣。

-- 如果還是獲取不到G, 就需要休眠M了, 接下來是休眠的步驟:

--- 再次檢查當前GC是否在標記階段, 在則查找有沒有待運行的GC Worker, GC Worker也是一個G;

--- 再次檢查如果當前GC需要停止整個世界, 或者P指定了需要再安全點運行的函數(shù), 則跳到findrunnable的頂部重試裂问;

--- 再次檢查全局運行隊列中是否有G, 有則獲取并返回侧啼;

--- 釋放M擁有的P, P會變?yōu)榭臻e(_Pidle)狀態(tài)牛柒;

--- 把P添加到"空閑P鏈表"中;

--- 讓M離開自旋狀態(tài), 這里的處理非常重要, 參考上面的"空閑M鏈表"痊乾;

--- 首先減少表示當前自旋中的M的數(shù)量的全局變量nmspinning皮壁;

--- 再次檢查所有P的本地運行隊列, 如果不為空則讓M重新進入自旋狀態(tài), 并跳findrunnable的頂部重試;

--- 再次檢查有沒有待運行的GC Worker, 有則讓M重新進入自旋狀態(tài), 并跳到findrunnable的頂部重試

--- 再次檢查網(wǎng)絡(luò)事件反應器是否有待運行的G, 這里對netpoll的調(diào)用會阻塞, 直到某個fd收到了事件哪审;

--- 如果最終還是獲取不到G, 調(diào)用stopm休眠當前的M蛾魄;

--- 喚醒后跳到findrunnable的頂部重試。

  • 成功獲取到一個待運行的G湿滓;

  • 讓M離開自旋狀態(tài), 調(diào)用resetspinning, 這里的處理和上面的不一樣:

-- 如果當前有空閑的P, 但是無自旋的M(nmspinning等于0), 則喚醒或新建一個M滴须;

-- 上面離開自旋狀態(tài)是為了休眠M, 所以會再次檢查所有隊列然后休眠;

-- 這里離開自選狀態(tài)是為了執(zhí)行G, 所以會檢查是否有空閑的P, 有則表示可以再開新的M執(zhí)行G叽奥。

  • 如果G要求回到指定的M(例如上面的runtime.main):

-- 調(diào)用startlockedm函數(shù)把G和P交給該M, 自己進入休眠扔水;

-- 從休眠喚醒后跳到schedule的頂部重試

  • 調(diào)用execute函數(shù)在當前M上執(zhí)行G。

在schedule方法中朝氓,m在以下情況會休眠:

  • 當m.lockedg != 0(m有綁定固定執(zhí)行的g)铭污,m會在stoplockedm()解綁p并休眠,等待被綁定的g被其他m調(diào)度的時候來喚醒該m膀篮,直接被綁定的g

  • sched.gcwaiting != 0(gc STW)m會休眠

  • findrunnable()中想盡一切辦法都沒有獲取到可執(zhí)行的g的時候,m會休眠

  • 獲取到g的時候岂膳,g綁定了其他的m(gp.lockedm != 0),當前m會解綁p誓竿,休眠,然后喚醒g綁定的m谈截,執(zhí)行該g

當m休眠被喚醒的時候筷屡,并不會從固定的位置開始執(zhí)行,會直接從休眠的位置開始執(zhí)行簸喂。

2.6.1 findrunnable

這里邏輯較為復雜毙死,下面將以代碼中的兩個標簽top和stop將流程分開:

image
image

2.6.2 sysmon

系統(tǒng)調(diào)用和搶占調(diào)度都離不開這個函數(shù),sysmon獨立的運行在一個特殊的m上喻鳄,它定期執(zhí)行一次扼倘,每次會做以下事情:

  • 釋放閑置超過5分鐘的span物理內(nèi)存

  • 如果超過2分鐘沒有垃圾回收,強制執(zhí)行

  • 將長時間未處理的netpoll結(jié)果添加到任務(wù)隊列

  • 向長時間運行的G任務(wù)發(fā)出搶占調(diào)度

  • 收回因syscall長時間阻塞的P

2.6.3 搶占式調(diào)度

在Go1.3版本以前除呵,調(diào)度器還不支持搶占式調(diào)度再菊,只能依靠 goroutine 主動讓出 CPU 資源,存在非常嚴重的調(diào)度問題:

  • 單獨的 goroutine 可以一直占用線程運行颜曾,不會切換到其他的 goroutine纠拔,造成饑餓問題

  • 垃圾回收需要暫停整個程序(Stop-the-world,STW)泛豪,如果沒有搶占可能需要等待幾分鐘的時間稠诲,導致整個程序無法工作

在Go1.3版本中侦鹏,當某個goroutine執(zhí)行超過10ms,sysmon會向其發(fā)起搶占調(diào)度請求臀叙,由于Go調(diào)度不像OS調(diào)度那樣有時間片的概念略水,因此實際搶占機制要弱很多: Go中的搶占實際上是為G設(shè)置搶占標記(g.stackguard0),當G調(diào)用某函數(shù)時(更確切說匹耕,在通過newstack分配函數(shù)棧時)聚请,被編譯器安插的指令會檢查這個標記,并且將當前G以runtime.Goched的方式暫停稳其,并加入到全局隊列驶赏。但是這種需要函數(shù)調(diào)用主動配合的調(diào)度方式存在一些邊緣情況,就比如說下面的例子:


package main

import (

    "runtime"

    "time"

)

func main() {

    runtime.GOMAXPROCS(1)

    go func() {

        for {

        }

    }()

    time.Sleep(time.Millisecond)

    println("OK")

}

其中創(chuàng)建一個goroutine并掛起既鞠, main goroutine 優(yōu)先調(diào)用了 休眠煤傍,此時唯一的 P 會轉(zhuǎn)去執(zhí)行 for 循環(huán)所創(chuàng)建的 goroutine,進而 main goroutine 永遠不會再被調(diào)度嘱蛋。換一句話說在Go1.14之前蚯姆,上邊的代碼永遠不會輸出OK,因為這種協(xié)作式的搶占式調(diào)度是不會使一個沒有主動放棄執(zhí)行權(quán)洒敏、且不參與任何函數(shù)調(diào)用的goroutine被搶占龄恋。

Go1.14 實現(xiàn)了基于信號的真搶占式調(diào)度解決了上述問題。Go1.14程序啟動時凶伙,在 runtime.sighandler 函數(shù)中注冊了 SIGURG 信號的處理函數(shù) runtime.doSigPreempt郭毕,在觸發(fā)垃圾回收的棧掃描時,調(diào)用函數(shù)掛起goroutine函荣,并向M發(fā)送信號显押,M收到信號后,會讓當前goroutine陷入休眠繼續(xù)執(zhí)行其他的goroutine傻挂。

SIGURG表示I/O緊急信號乘碑,屬于高優(yōu)先級信號,在進程忙時仍然會被優(yōu)先處理金拒,默認在Unix系統(tǒng)進程中是被忽略的兽肤,因此被Golang用于處理搶占式調(diào)度。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末殖蚕,一起剝皮案震驚了整個濱河市轿衔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌睦疫,老刑警劉巖害驹,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛤育,居然都是意外死亡宛官,警方通過查閱死者的電腦和手機葫松,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來底洗,“玉大人腋么,你說我怎么就攤上這事『ヒ荆” “怎么了珊擂?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長费变。 經(jīng)常有香客問我摧扇,道長,這世上最難降的妖魔是什么挚歧? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任扛稽,我火速辦了婚禮,結(jié)果婚禮上滑负,老公的妹妹穿的比我還像新娘在张。我一直安慰自己,他們只是感情好矮慕,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布帮匾。 她就那樣靜靜地躺著,像睡著了一般痴鳄。 火紅的嫁衣襯著肌膚如雪辟狈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天夏跷,我揣著相機與錄音,去河邊找鬼明未。 笑死槽华,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的趟妥。 我是一名探鬼主播猫态,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼披摄!你這毒婦竟也來了亲雪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤疚膊,失蹤者是張志新(化名)和其女友劉穎义辕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寓盗,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡灌砖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年璧函,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片基显。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蘸吓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出撩幽,到底是詐尸還是另有隱情库继,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布窜醉,位于F島的核電站宪萄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酱虎。R本人自食惡果不足惜雨膨,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望读串。 院中可真熱鬧聊记,春花似錦、人聲如沸恢暖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杰捂。三九已至舆床,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嫁佳,已是汗流浹背挨队。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蒿往,地道東北人盛垦。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像瓤漏,于是被迫代替她去往敵國和親腾夯。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

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