Kotlin 協(xié)程

為什么要搞出和用協(xié)程呢

是節(jié)省CPU霞怀,避免系統(tǒng)內(nèi)核級的線程頻繁切換,造成的CPU資源浪費(fèi)莉给。好鋼用在刀刃上毙石。而協(xié)程是用戶態(tài)的線程,用戶可以自行控制協(xié)程的創(chuàng)建于銷毀颓遏,極大程度避免了系統(tǒng)級線程上下文切換造成的資源浪費(fèi)徐矩。

是節(jié)約內(nèi)存,在64位的Linux中州泊,一個(gè)線程需要分配8MB棧內(nèi)存和64MB堆內(nèi)存丧蘸,系統(tǒng)內(nèi)存的制約導(dǎo)致我們無法開啟更多線程實(shí)現(xiàn)高并發(fā)。而在協(xié)程編程模式下,可以輕松有十幾萬協(xié)程力喷,這是線程無法比擬的刽漂。

是穩(wěn)定性,前面提到線程之間通過內(nèi)存來共享數(shù)據(jù)弟孟,這也導(dǎo)致了一個(gè)問題贝咙,任何一個(gè)線程出錯(cuò)時(shí),進(jìn)程中的所有線程都會跟著一起崩潰拂募。

是開發(fā)效率庭猩,使用協(xié)程在開發(fā)程序之中,可以很方便的將一些耗時(shí)的IO操作異步化陈症,例如寫文件蔼水、耗時(shí)IO請求等。

對于協(xié)程的一個(gè)總結(jié)

特征:協(xié)程是運(yùn)行在單線程中的并發(fā)程序

優(yōu)點(diǎn):省去了傳統(tǒng) Thread 多線程并發(fā)機(jī)制中切換線程時(shí)帶來的線程上下文切換录肯、線程狀態(tài)切換趴腋、Thread 初始化上的性能損耗,能大幅度的提高并發(fā)性能

簡單理解:在單線程上由程序員自己調(diào)度運(yùn)行的并行計(jì)算

寫到最后

協(xié)程本身不是替換線程的,因?yàn)閰f(xié)程是建立在線程之上的,但是協(xié)程能夠更好的為我們提供執(zhí)行高并發(fā)任務(wù)

1.kotlin中協(xié)程的特點(diǎn):可以用同步的方式寫出異步的代碼

coroutineScope.launch(Dispatchers.Main){// 開始協(xié)程:主線程

? ? val token=api.getToken()// 網(wǎng)絡(luò)請求:IO 線程

? ? val user=api.getUser(token)// 網(wǎng)絡(luò)請求:IO 線程

? ? nameTv.text=user.name// 更新 UI:主線程

}

2.協(xié)程中掛起的本質(zhì)

啟動一個(gè)協(xié)程可以使用 launch 或者 async 函數(shù)论咏,協(xié)程其實(shí)就是這兩個(gè)函數(shù)中閉包的代碼塊优炬。

launch ,async 或者其他函數(shù)創(chuàng)建的協(xié)程厅贪,在執(zhí)行到某一個(gè) suspend 函數(shù)的時(shí)候蠢护,這個(gè)協(xié)程會被「suspend」,也就是被掛起养涮。

3.協(xié)程的代碼塊中葵硕,線程執(zhí)行到了 suspend 函數(shù)這里的時(shí)候,就暫時(shí)不再執(zhí)行剩余的協(xié)程代碼单寂,跳出協(xié)程的代碼塊贬芥。

那線程接下來會做什么呢?

如果它是一個(gè)后臺線程:

要么無事可做宣决,被系統(tǒng)回收

要么繼續(xù)執(zhí)行別的后臺任務(wù)

跟 Java 線程池里的線程在工作結(jié)束之后是完全一樣的:回收或者再利用蘸劈。

如果這個(gè)線程它是 Android 的主線程,那它接下來就會繼續(xù)回去工作:也就是一秒鐘 60 次的界面刷新任務(wù)尊沸。

一個(gè)常見的場景是威沫,獲取一個(gè)圖片,然后顯示出來:

// 主線程中

GlobalScope.launch(Dispatchers.Main){

? ? valimage=suspendingGetImage(imageId)// 獲取圖片

? ? avatarIv.setImageBitmap(image)// 顯示出來

}

suspend fun suspendingGetImage(id:String)=withContext(Dispatchers.IO){...}

協(xié)程:

線程的代碼在到達(dá)suspend函數(shù)的時(shí)候被掐斷洼专,接下來協(xié)程會從這個(gè)suspend函數(shù)開始繼續(xù)往下執(zhí)行棒掠,不過是在指定的線程

誰指定的屁商?是suspend函數(shù)指定的烟很,比如我們這個(gè)例子中,函數(shù)內(nèi)部的withContext傳入的Dispatchers.IO所指定的 IO 線程。

Dispatchers調(diào)度器雾袱,它可以將協(xié)程限制在一個(gè)特定的線程執(zhí)行恤筛,或者將它分派到一個(gè)線程池,或者讓它不受限制地運(yùn)行芹橡,關(guān)于Dispatchers這里先不展開了毒坛。

那我們平日里常用到的調(diào)度器有哪些?

常用的Dispatchers林说,有以下三種:

Dispatchers.Main:Android 中的主線程

Dispatchers.IO:針對磁盤和網(wǎng)絡(luò) IO 進(jìn)行了優(yōu)化煎殷,適合 IO 密集型的任務(wù),比如:讀寫文件腿箩,操作數(shù)據(jù)庫以及網(wǎng)絡(luò)請求

Dispatchers.Default:適合 CPU 密集型的任務(wù)豪直,比如計(jì)算

回到我們的協(xié)程,它從suspend函數(shù)開始脫離啟動它的線程度秘,繼續(xù)執(zhí)行在Dispatchers所指定的 IO 線程顶伞。

緊接著在suspend函數(shù)執(zhí)行完成之后饵撑,協(xié)程為我們做的最爽的事就來了:會自動幫我們把線程再切回來剑梳。

這個(gè)「切回來」是什么意思?

我們的協(xié)程原本是運(yùn)行在主線程的滑潘,當(dāng)代碼遇到 suspend 函數(shù)的時(shí)候垢乙,發(fā)生線程切換,根據(jù)Dispatchers切換到了 IO 線程语卤;

當(dāng)這個(gè)函數(shù)執(zhí)行完畢后追逮,線程又切了回來,「切回來」也就是協(xié)程會幫我再post一個(gè)Runnable粹舵,讓我剩下的代碼繼續(xù)回到主線程去執(zhí)行钮孵。

我們從線程和協(xié)程的兩個(gè)角度都分析完成后,終于可以對協(xié)程的「掛起」suspend 做一個(gè)解釋:

協(xié)程在執(zhí)行到有 suspend 標(biāo)記的函數(shù)的時(shí)候眼滤,會被 suspend 也就是被掛起巴席,而所謂的被掛起,就是切個(gè)線程诅需;

不過區(qū)別在于漾唉,掛起函數(shù)在執(zhí)行完成之后,協(xié)程會重新切回它原先的線程堰塌。

再簡單來講赵刑,在 Kotlin 中所謂的掛起,就是一個(gè)稍后會被自動切回來的線程調(diào)度操作场刑。

這個(gè)「切回來」的動作般此,在 Kotlin 里叫做 resume,恢復(fù)。

通過剛才的分析我們知道:掛起之后是需要恢復(fù)铐懊。

而恢復(fù)這個(gè)功能是協(xié)程的屎勘,如果你不在協(xié)程里面調(diào)用,恢復(fù)這個(gè)功能沒法實(shí)現(xiàn)居扒,所以也就回答了這個(gè)問題:為什么掛起函數(shù)必須在協(xié)程或者另一個(gè)掛起函數(shù)里被調(diào)用概漱。

再細(xì)想下這個(gè)邏輯:一個(gè)掛起函數(shù)要么在協(xié)程里被調(diào)用,要么在另一個(gè)掛起函數(shù)里被調(diào)用喜喂,那么它其實(shí)直接或者間接地瓤摧,總是會在一個(gè)協(xié)程里被調(diào)用的。

所以玉吁,要求suspend函數(shù)只能在協(xié)程里或者另一個(gè) suspend 函數(shù)里被調(diào)用照弥,還是為了要讓協(xié)程能夠在suspend函數(shù)切換線程之后再切回

通過剛才的分析我們知道:掛起之后是需要恢復(fù)。

而恢復(fù)這個(gè)功能是協(xié)程的进副,如果你不在協(xié)程里面調(diào)用这揣,恢復(fù)這個(gè)功能沒法實(shí)現(xiàn),所以也就回答了這個(gè)問題:為什么掛起函數(shù)必須在協(xié)程或者另一個(gè)掛起函數(shù)里被調(diào)用影斑。

再細(xì)想下這個(gè)邏輯:一個(gè)掛起函數(shù)要么在協(xié)程里被調(diào)用给赞,要么在另一個(gè)掛起函數(shù)里被調(diào)用,那么它其實(shí)直接或者間接地矫户,總是會在一個(gè)協(xié)程里被調(diào)用的片迅。

所以,要求suspend函數(shù)只能在協(xié)程里或者另一個(gè) suspend 函數(shù)里被調(diào)用皆辽,還是為了要讓協(xié)程能夠在 suspend 函數(shù)切換線程之后再切回來柑蛇。

什么是「非阻塞式掛起」

非阻塞式是相對阻塞式而言的。

編程語言中的很多概念其實(shí)都來源于生活驱闷,就像脫口秀的段子一樣耻台。

線程阻塞很好理解,現(xiàn)實(shí)中的例子就是交通堵塞空另,它的核心有 3 點(diǎn):

前面有障礙物盆耽,你過不去(線程卡了)

需要等障礙物清除后才能過去(耗時(shí)任務(wù)結(jié)束)

除非你繞道而行(切到別的線程)

從語義上理解「非阻塞式掛起」,講的是「非阻塞式」這個(gè)是掛起的一個(gè)特點(diǎn)痹换,也就是說征字,協(xié)程的掛起,就是非阻塞式的娇豫,協(xié)程是不講「阻塞式的掛起」的概念的匙姜。

我們講「非阻塞式掛起」,其實(shí)它有幾個(gè)前提:并沒有限定在一個(gè)線程里說這件事冯痢,因?yàn)閽炱疬@件事氮昧,本來就是涉及到多線程框杜。

就像視頻里講的,阻塞不阻塞袖肥,都是針對單線程講的咪辱,一旦切了線程,肯定是非阻塞的椎组,你都跑到別的線程了油狂,之前的線程就自由了,可以繼續(xù)做別的事情了寸癌。

所以「非阻塞式掛起」专筷,其實(shí)就是在講協(xié)程在掛起的同時(shí)切線程這件事情。

為什么要講非阻塞式掛起

「非阻塞式掛起」和第二篇的「掛起要切線程」是同一件事情蒸苇,那還有講的必要嗎磷蛹?

是有的。因?yàn)樗趯懛ㄉ虾蛦尉€程的阻塞式是一樣的溪烤。

協(xié)程只是在寫法上「看起來阻塞」味咳,其實(shí)是「非阻塞」的,因?yàn)樵趨f(xié)程里面它做了很多工作檬嘀,其中有一個(gè)就是幫我們切線程槽驶。

之前說的掛起,重點(diǎn)是說切線程先切過去枪眉,然后再切回來捺檬。

而這里的非阻塞式,重點(diǎn)是說線程雖然會切贸铜,但寫法上和普通的單線程差不多。

讓我們來看看下面的例子:

main{

GlobalScope.launch(Dispatchers.Main){// 耗時(shí)操作val user=suspendingRequestUser()

updateView(user)

}

private suspend fun suspendingRequestUser():User=withContext(Dispatchers.IO){api.requestUser()}}

阻塞的本質(zhì)

首先聂受,所有的代碼本質(zhì)上都是阻塞式的蒿秦,而只有比較耗時(shí)的代碼才會導(dǎo)致人類可感知的等待,比如在主線程上做一個(gè)耗時(shí) 50 ms 的操作會導(dǎo)致界面卡掉幾幀蛋济,這種是我們?nèi)搜勰苡^察出來的棍鳖,而這就是我們通常意義所說的「阻塞」。

舉個(gè)例子碗旅,當(dāng)你開發(fā)的 app 在性能好的手機(jī)上很流暢渡处,在性能差的老手機(jī)上會卡頓,就是在說同一行代碼執(zhí)行的時(shí)間不一樣祟辟。

視頻中講了一個(gè)網(wǎng)絡(luò) IO 的例子医瘫,IO 阻塞更多是反映在「等」這件事情上,它的性能瓶頸是和網(wǎng)絡(luò)的數(shù)據(jù)交換旧困,你切多少個(gè)線程都沒用醇份,該花的時(shí)間一點(diǎn)都少不了稼锅。

而這跟協(xié)程半毛錢關(guān)系沒有,切線程解決不了的事情僚纷,協(xié)程也解決不了

總結(jié)

關(guān)于這邊文章的標(biāo)題協(xié)程是什么矩距、掛起是什么、掛起的非阻塞式可以做下面的總結(jié)

協(xié)程是什么

協(xié)程就是切線程怖竭;

掛起是什么

掛起就是可以自動切回來的切線程锥债;

掛起的非阻塞式

掛起的非阻塞式指的是它能用看起來阻塞的代碼寫出非阻塞的操作。

2.在kotlin使用協(xié)程

項(xiàng)目中配置對 Kotlin 協(xié)程的支持

在使用協(xié)程之前痊臭,我們需要在 build.gradle 文件中增加對 Kotlin 協(xié)程的依賴:

項(xiàng)目根目錄下的 build.gradle :

buildscript {

? ? ext.kotlin_coroutines = '1.4.0'

}

Module 下的 build.gradle

dependencies {

? ? ? ? implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'

}

創(chuàng)建協(xié)程

kotlin 中 GlobalScope 類提供了幾個(gè)攜程構(gòu)造函數(shù):

launch - 創(chuàng)建協(xié)程

async - 創(chuàng)建帶返回值的協(xié)程赞弥,返回的是 Deferred 類

withContext - 不創(chuàng)建新的協(xié)程,指定協(xié)程上運(yùn)行代碼塊

runBlocking - 不是 GlobalScope 的 API趣兄,可以獨(dú)立使用绽左,區(qū)別是 runBlocking 里面的 delay 會阻塞線程,而 launch 創(chuàng)建的不會

先跑起來一個(gè)簡單的例子:

import kotlinx.coroutines.*

fun main(){

? ? ? ? GlobalScope.launch{

? ? ? ? // 在后臺啟動一個(gè)新的協(xié)程并繼續(xù)

? ? ? ? delay(1000L)// 非阻塞的等待 1 秒鐘(默認(rèn)時(shí)間單是毫秒)

? ? ? ? println("World!")// 在延遲后打印輸出}

? ? ? ? println("Hello,")// 協(xié)程已在等待時(shí)主線程還在繼續(xù)

? ? ? ? Thread.sleep(2000L)// 阻塞主線程 2 秒鐘來保證 JVM 存活

}

協(xié)程中卻有一個(gè)很實(shí)用的函數(shù):withContext 艇潭。這個(gè)函數(shù)可以切換到指定的線程拼窥,并在閉包內(nèi)的邏輯執(zhí)行結(jié)束之后,自動把線程切回去繼續(xù)執(zhí)行蹋凝。那么可以將上面的代碼寫成這樣:

coroutineScope.launch(

? ? Dispatchers.Main){//? 在 UI 線程開始

? ? val image=withContext(Dispatchers.IO){// 切換到 IO 線程鲁纠,并在執(zhí)行完成后切回 UI 線程

? ? getImage(imageId)// 將會運(yùn)行在 IO 線程}

? ? avatarIv.setImageBitmap(image)// 回到 UI 線程更新 UI

}

我們甚至可以把 withContext 放進(jìn)一個(gè)單獨(dú)的函數(shù)里面:

launch(Dispatchers.Main){//? 在 UI 線程開始

val image=getImage(imageId)

avatarIv.setImageBitmap(image)//? 執(zhí)行結(jié)束后,自動切換回 UI 線程}//

suspend fun getImage(imageId:Int)=withContext(Dispatchers.IO){...}


launch 函數(shù)

launch 函數(shù)定義

public fun CoroutineScope.launch(

? ? context:CoroutineContext=EmptyCoroutineContext,

? ? start:CoroutineStart=CoroutineStart.DEFAULT,

? ? block:suspendCoroutineScope.()->Unit):Job

launch 是個(gè)擴(kuò)展函數(shù)鳍寂,接受3個(gè)參數(shù)改含,前面2個(gè)是常規(guī)參數(shù),最后一個(gè)是個(gè)對象式函數(shù)迄汛,這樣的話 kotlin 就可以使用以前說的閉包的寫法:() 里面寫常規(guī)參數(shù)捍壤,{} 里面寫函數(shù)式對象的實(shí)現(xiàn),就像上面的例子一樣

我們需要關(guān)心的是 launch 的3個(gè)參數(shù)和返回值 Job:

CoroutineContext - 可以理解為協(xié)程的上下文鞍爱,在這里我們可以設(shè)置 CoroutineDispatcher 協(xié)程運(yùn)行的線程調(diào)度器鹃觉,有 4種線程模式:

Dispatchers.Default

Dispatchers.IO -

Dispatchers.Main - 主線程

Dispatchers.Unconfined - 沒指定,就是在當(dāng)前線程

不寫的話就是 Dispatchers.Default 模式的睹逃,或者我們可以自己創(chuàng)建協(xié)程上下文盗扇,也就是線程池,newSingleThreadContext 單線程沉填,newFixedThreadPoolContext 線程池

CoroutineStart - 啟動模式疗隶,默認(rèn)是DEAFAULT,也就是創(chuàng)建就啟動翼闹;還有一個(gè)是LAZY斑鼻,意思是等你需要它的時(shí)候,再調(diào)用啟動

DEAFAULT - 模式模式橄碾,不寫就是默認(rèn)

ATOMIC -

UNDISPATCHED

LAZY - 懶加載模式卵沉,你需要它的時(shí)候颠锉,再調(diào)用啟動

block - 閉包方法體,定義協(xié)程內(nèi)需要執(zhí)行的操作

Job - 協(xié)程構(gòu)建函數(shù)的返回值史汗,可以把 Job 看成協(xié)程對象本身琼掠,協(xié)程的操作方法都在 Job 身上了

job.start() - 啟動協(xié)程,除了 lazy 模式停撞,協(xié)程都不需要手動啟動

job.join() - 等待協(xié)程執(zhí)行完畢

job.cancel() - 取消一個(gè)協(xié)程

job.cancelAndJoin() - 等待協(xié)程執(zhí)行完畢然后再取消

創(chuàng)建一個(gè)協(xié)程

創(chuàng)建該launch函數(shù)返回了一個(gè)可以被用來取消運(yùn)行中的協(xié)程的Job

val job=launch{

? ? repeat(1000){i->

? ? ? ? println("job: I'm sleeping$i...")

? ? ? ? delay(500L)

? ? }

}

取消

val? job=launch{

repeat(1000){

i->println("job: I'm sleeping $i ...")

delay(500L)}}

delay(1300L)// 延遲一段時(shí)間

println("main: I'm tired of waiting!")job.cancel()// 取消該作業(yè)

job.join()// 等待作業(yè)執(zhí)行結(jié)束

println("main: Now I can quit.")

程序執(zhí)行后的輸出如下:

job: I'm sleeping 0 ...

job: I'm sleeping 1 ...

job: I'm sleeping 2 ...

main: I'm tired of waiting!

main: Now I can quit.

一旦 main 函數(shù)調(diào)用了 job.cancel瓷蛙,我們在其它的協(xié)程中就看不到任何輸出,因?yàn)樗蝗∠?/p>

超時(shí)

在實(shí)踐中絕大多數(shù)取消一個(gè)協(xié)程的理由是它有可能超時(shí)戈毒。 當(dāng)你手動追蹤一個(gè)相關(guān) Job的引用并啟動了一個(gè)單獨(dú)的協(xié)程在延遲后取消追蹤艰猬,這里已經(jīng)準(zhǔn)備好使用 withTimeout 函數(shù)來做這件事。 來看看示例代碼:

withTimeout(1300L){repeat(1000){i->

println("I'm sleeping $i ...")

delay(500L)}}

運(yùn)行后得到如下輸出:

I'm sleeping 0 ...

I'm sleeping 1 ...

I'm sleeping 2 ...

Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms

這里我們看到了TimeoutCancellationException異常,這異常是因?yàn)槌瑫r(shí)導(dǎo)致的異常取消

解決這個(gè)問題也很簡單通過withTimeout的withTimeoutOrNull函數(shù),代碼示例:

valresult=withTimeoutOrNull(1300L){repeat(1000){i->println("I'm sleeping$i...")delay(500L)}"Done"http:// 在它運(yùn)行得到結(jié)果之前取消它}println("Result is$result")

運(yùn)行后得到如下輸出:

I'm sleeping 0 ...

I'm sleeping 1 ...

I'm sleeping 2 ...

Result is null

這樣就沒有拋出異常了


async 函數(shù)定義

public fun <T> CoroutineScope.async(

? ? context:CoroutineContext=EmptyCoroutineContext,

? ? start:CoroutineStart=CoroutineStart.DEFAULT,

? ? block:suspendCoroutineScope.()->T):Deferred<T>{

? ? ? ? val? newContext=newCoroutineContext(context)

? ? ? ? val coroutine=if(start.isLazy){

? ? ? ? LazyDeferredCoroutine(newContext,block)

}else{DeferredCoroutine<T>(newContext,active=true)

? ? ? ? coroutine.start(start,coroutine,block)

? ? ? ? return coroutine

}

從源碼可以看出launch 和 async的唯一區(qū)別在于async的返回值

async 返回的是 Deferred 類型埋市,Deferred 繼承自 Job 接口冠桃,Job有的它都有,增加了一個(gè)方法 await 道宅,這個(gè)方法接收的是 async 閉包中返回的值食听,async 的特點(diǎn)是不會阻塞當(dāng)前線程,但會阻塞所在協(xié)程污茵,也就是掛起

但是需要注意的是async 并不會阻塞線程樱报,只是阻塞鎖調(diào)用的協(xié)程

async和launch的區(qū)別

launch 更多是用來發(fā)起一個(gè)無需結(jié)果的耗時(shí)任務(wù),這個(gè)工作不需要返回結(jié)果泞当。

async 函數(shù)則是更進(jìn)一步迹蛤,用于異步執(zhí)行耗時(shí)任務(wù),并且需要返回值(如網(wǎng)絡(luò)請求襟士、數(shù)據(jù)庫讀寫盗飒、文件讀寫),在執(zhí)行完畢通過 await() 函數(shù)獲取返回值敌蜂。

runBlocking

runBlocking啟動的協(xié)程任務(wù)會阻斷當(dāng)前線程箩兽,直到該協(xié)程執(zhí)行結(jié)束。當(dāng)協(xié)程執(zhí)行結(jié)束之后章喉,頁面才會被顯示出來。

runBlocking 通常適用于單元測試的場景身坐,而業(yè)務(wù)開發(fā)中不會用到這個(gè)函數(shù)

relay秸脱、yield

relay 和 yield 方法是協(xié)程內(nèi)部的操作,可以掛起協(xié)程部蛇,

relay摊唇、yield 的區(qū)別

relay 是掛起協(xié)程并經(jīng)過執(zhí)行時(shí)間恢復(fù)協(xié)程,當(dāng)線程空閑時(shí)就會運(yùn)行協(xié)程

yield 是掛起協(xié)程涯鲁,讓協(xié)程放棄本次 cpu 執(zhí)行機(jī)會讓給別的協(xié)程巷查,當(dāng)線程空閑時(shí)再次運(yùn)行協(xié)程有序。

我們只要使用 kotlin 提供的協(xié)程上下文類型,線程池是有多個(gè)線程的岛请,再次執(zhí)行的機(jī)會很快就會有的旭寿。

除了 main 類型,協(xié)程在掛起后都會封裝成任務(wù)放到協(xié)程默認(rèn)線程池的任務(wù)隊(duì)列里去崇败,有延遲時(shí)間的在時(shí)間過后會放到隊(duì)列里去盅称,沒有延遲時(shí)間的直接放到隊(duì)列里去


原文鏈接:http://www.reibang.com/p/402a69dbd66d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市后室,隨后出現(xiàn)的幾起案子缩膝,更是在濱河造成了極大的恐慌,老刑警劉巖岸霹,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疾层,死亡現(xiàn)場離奇詭異,居然都是意外死亡贡避,警方通過查閱死者的電腦和手機(jī)痛黎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贸桶,“玉大人舅逸,你說我怎么就攤上這事』噬福” “怎么了琉历?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長水醋。 經(jīng)常有香客問我旗笔,道長,這世上最難降的妖魔是什么拄踪? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任蝇恶,我火速辦了婚禮,結(jié)果婚禮上惶桐,老公的妹妹穿的比我還像新娘撮弧。我一直安慰自己,他們只是感情好姚糊,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布贿衍。 她就那樣靜靜地躺著,像睡著了一般救恨。 火紅的嫁衣襯著肌膚如雪贸辈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天肠槽,我揣著相機(jī)與錄音擎淤,去河邊找鬼奢啥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嘴拢,可吹牛的內(nèi)容都是我干的桩盲。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼炊汤,長吁一口氣:“原來是場噩夢啊……” “哼正驻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抢腐,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤姑曙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后迈倍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體伤靠,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年啼染,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宴合。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,444評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡迹鹅,死狀恐怖卦洽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斜棚,我是刑警寧澤阀蒂,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站弟蚀,受9級特大地震影響蚤霞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜义钉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一昧绣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捶闸,春花似錦夜畴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至醉锅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間发绢,已是汗流浹背硬耍。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工垄琐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人经柴。 一個(gè)月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓狸窘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坯认。 傳聞我的和親對象是個(gè)殘疾皇子翻擒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,455評論 2 359

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

  • [TOC] 簡介 Coroutines are computer program components that ...
    Whyn閱讀 5,942評論 5 15
  • 在今年的三月份,我因?yàn)樾枰獮轫?xiàng)目搭建一個(gè)新的網(wǎng)絡(luò)請求框架開始接觸 Kotlin 協(xié)程牛哺。那時(shí)我司項(xiàng)目中同時(shí)存在著兩種...
    Android開發(fā)指南閱讀 839評論 0 2
  • 在今年的三月份陋气,我因?yàn)樾枰獮轫?xiàng)目搭建一個(gè)新的網(wǎng)絡(luò)請求框架開始接觸 Kotlin 協(xié)程。那時(shí)我司項(xiàng)目中同時(shí)存在著兩種...
    業(yè)志陳閱讀 1,073評論 0 5
  • 協(xié)程是輕量級的線程引润。 kotlin協(xié)程是kotlin的擴(kuò)展庫(kotlinx.coroutines)巩趁。 線程在An...
    付小影子閱讀 6,423評論 0 4
  • 久違的晴天,家長會淳附。 家長大會開好到教室時(shí)议慰,離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長分享經(jīng)驗(yàn)奴曙。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,524評論 16 22