Objective-C高級(jí)編程讀書(shū)筆記之GCD

Objective-C高級(jí)編程 iOS與OS X多線(xiàn)程和內(nèi)存管理

Objective-C高級(jí)編程讀書(shū)筆記三部曲已經(jīng)寫(xiě)完, 另外兩篇如下 :
Objective-C高級(jí)編程讀書(shū)筆記之內(nèi)存管理
Objective-C高級(jí)編程讀書(shū)筆記之blocks


Grand Central Dispatch (GCD)

目錄

  1. 什么是GCD
  2. 什么是多線(xiàn)程, 并發(fā)
  3. GCD的優(yōu)勢(shì)
  4. GCD的API介紹
  5. GCD的注意點(diǎn)
  6. GCD的使用場(chǎng)景
  7. Dispatch Source
  8. 總結(jié)

1. 什么是GCD

GCD, Grand Central Dispatch, 可譯為"強(qiáng)大的中樞調(diào)度器", 基于libdispatch, 純C語(yǔ)言, 里面包含了許多多線(xiàn)程相關(guān)非常強(qiáng)大的函數(shù). 程序員可以既不寫(xiě)一句線(xiàn)程管理的代碼又能很好地使用多線(xiàn)程執(zhí)行任務(wù).

GCD中有Dispatch QueueDispatch Source. Dispatch Queue是主要的, 而Dispatch Source比較次要. 所以這里主要介紹Dispatch Queue, 而Dispatch Source在下面會(huì)簡(jiǎn)單介紹.

Dispatch Queue

蘋(píng)果官方對(duì)GCD的說(shuō)明如下 :

開(kāi)發(fā)者要做的只是定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)腄ispatch Queue中.

這句話(huà)用源代碼表示如下

dispatch_async(queue, ^{
    /*
     * 想執(zhí)行的任務(wù)
     */  
});

該源碼用block的語(yǔ)法定義想執(zhí)行的任務(wù)然后通過(guò)dispatch_async函數(shù)講任務(wù)追加到賦值在變量queue的"Dispatch Queue"中.

Dispatch Queue究竟是什么???

Dispatch Queue是執(zhí)行處理的等待隊(duì)列, 按照先進(jìn)先出(FIFO, First-In-First-Out)的順序進(jìn)行任務(wù)處理.

First-In-First-Out

另外, 隊(duì)列分兩種, 一種是串行隊(duì)列(Serial Dispatch Queue), 一種是并行隊(duì)列(Concurrent Dispatch Queue).

Dispatch Queue的種類(lèi) 說(shuō)明
Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束
Concurrent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束
串行隊(duì)列

串行隊(duì)列 : 讓任務(wù)一個(gè)接一個(gè)執(zhí)行

并行隊(duì)列

并發(fā)隊(duì)列 : 讓多個(gè)任務(wù)同時(shí)執(zhí)行(自動(dòng)開(kāi)啟多個(gè)線(xiàn)程執(zhí)行任務(wù))
并發(fā)功能只有在異步函數(shù)(dispatch_async)下才有效(想想看為什么?)

GCD的API會(huì)在下面詳細(xì)說(shuō)明~


2. 什么是多線(xiàn)程, 并發(fā)

我們知道, 一個(gè)應(yīng)用就相當(dāng)于一個(gè)進(jìn)程, 而一個(gè)進(jìn)程可以同時(shí)分發(fā)幾個(gè)線(xiàn)程同時(shí)處理任務(wù).而并發(fā)正是一個(gè)進(jìn)程開(kāi)啟多個(gè)線(xiàn)程同時(shí)執(zhí)行任務(wù)的意思, 主線(xiàn)程專(zhuān)門(mén)用來(lái)刷新UI,處理觸摸事件等 而子線(xiàn)程呢, 則用來(lái)執(zhí)行耗時(shí)的操作, 例如訪(fǎng)問(wèn)數(shù)據(jù)庫(kù), 下載數(shù)據(jù)等..

以前我們CPU還是單核的時(shí)候, 并不存在真正的線(xiàn)程并行, 因?yàn)槲覀冎挥幸粋€(gè)核, 一次只能處理一個(gè)任務(wù). 所以當(dāng)時(shí)我們計(jì)算機(jī)是通過(guò)分時(shí)也就是CPU地在各個(gè)進(jìn)程之間快速切換, 給人一種能同時(shí)處理多任務(wù)的錯(cuò)覺(jué)來(lái)實(shí)現(xiàn)的, 而現(xiàn)在多核CPU計(jì)算機(jī)則能真真正正貨真價(jià)實(shí)地辦到同時(shí)處理多個(gè)任務(wù).


3. GCD的優(yōu)勢(shì)

說(shuō)到優(yōu)勢(shì), 當(dāng)然有比較, 才能顯得出優(yōu)勢(shì)所在. 事實(shí)上, iOS中我們能使用的多線(xiàn)程管理技術(shù)有

  • pthread
  • NSThread
  • GCD
  • NSOperationQueue

pthread

來(lái)自Clang, 純C語(yǔ)言, 需要手動(dòng)創(chuàng)建線(xiàn)程, 銷(xiāo)毀線(xiàn)程, 手動(dòng)進(jìn)行線(xiàn)程管理. 而且代碼極其惡心, 我保證你寫(xiě)一次不想寫(xiě)第二次...不好意思我先去吐會(huì)T~T

NSThread :

Foundation框架下的OC對(duì)象, 依舊需要自己進(jìn)行線(xiàn)程管理,線(xiàn)程同步慧起。 線(xiàn)程同步對(duì)數(shù)據(jù)的加鎖會(huì)有一定的開(kāi)銷(xiāo)崩瓤。

GCD :

兩個(gè)字, 牛逼, 雖然是純C語(yǔ)言, 但是它用難以置信的非常簡(jiǎn)潔的方式實(shí)現(xiàn)了極其復(fù)雜的多線(xiàn)程編程, 而且還支持block內(nèi)聯(lián)形式進(jìn)行制定任務(wù). 簡(jiǎn)潔! 高效! 而且我們?cè)僖膊挥檬謩?dòng)進(jìn)行線(xiàn)程管理了.

NSOperationQueue :

相當(dāng)于Foundation框架的GCD, 以面向?qū)ο蟮恼Z(yǔ)法對(duì)GCD進(jìn)行了封裝. 效率一樣高.

GCD優(yōu)勢(shì)在哪里?

  1. GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核
  2. GCD會(huì)自動(dòng)管理線(xiàn)程的生命周期
  3. 使用方法及其簡(jiǎn)單

怎么樣? 心動(dòng)不, 迫不及待想要知道怎么使用GCD了吧, 那我們馬上切入正題~


4. GCD的API介紹

在介紹GCD的API之前, 我們先搞清楚四個(gè)名詞: 串行, 并行, 同步, 異步

  • 串行 : 一個(gè)任務(wù)執(zhí)行完, 再執(zhí)行下一個(gè)任務(wù)
  • 并行 : 多個(gè)任務(wù)同時(shí)執(zhí)行
  • 同步 : 在當(dāng)前線(xiàn)程中執(zhí)行任務(wù), 不具備開(kāi)啟線(xiàn)程的能力
  • 異步 : 在新的線(xiàn)程中執(zhí)行任務(wù), 具備開(kāi)啟線(xiàn)程的能力
串行, 并行, 同步, 異步的關(guān)系

下面開(kāi)始介紹GCD的API

創(chuàng)建隊(duì)列

dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)

手動(dòng)創(chuàng)建一個(gè)隊(duì)列.

  • label : 隊(duì)列的標(biāo)識(shí)符, 日后可用來(lái)調(diào)試程序
  • attr : 隊(duì)列類(lèi)型
    DISPATCH_QUEUE_CONCURRENT : 并發(fā)隊(duì)列
    DISPATCH_QUEUE_SERIAL 或 NULL : 串行隊(duì)列

需要注意的是, 通過(guò)dispatch_queue_create函數(shù)生成的queue在使用結(jié)束后需要通過(guò)dispatch_release函數(shù)來(lái)釋放.(只有在MRC下才需要釋放)

并不是什么時(shí)候都需要手動(dòng)創(chuàng)建隊(duì)列, 事實(shí)上系統(tǒng)給我們提供2個(gè)很常用的隊(duì)列.

主隊(duì)列

dispatch_get_main_queue();

該方法返回的是主線(xiàn)程中執(zhí)行的同步隊(duì)列. 用戶(hù)界面的更新等一些必須在主線(xiàn)程中執(zhí)行的操作追加到此隊(duì)列中.

全局并發(fā)隊(duì)列

dispatch_get_global_queue(long identifier, unsigned long flags);

該方法返回的是全局并發(fā)隊(duì)列. 使用十分廣泛.

  • identifier : 優(yōu)先級(jí)
    DISPATCH_QUEUE_PRIORITY_HIGH : 高優(yōu)先級(jí)
    DISPATCH_QUEUE_PRIORITY_DEFAULT : 默認(rèn)優(yōu)先級(jí)
    DISPATCH_QUEUE_PRIORITY_LOW : 低優(yōu)先級(jí)
    DISPATCH_QUEUE_PRIORITY_BACKGROUND : 后臺(tái)優(yōu)先級(jí)
  • flags : 暫時(shí)用不上, 傳 0 即可

注意 : 對(duì)Main Dispatch Queue和Global Dispatch Queue執(zhí)行dispatch_release和dispatch_retain沒(méi)有任何問(wèn)題. (MRC)

同步函數(shù)

dispatch_sync(dispatch_queue_t queue, ^(void)block);

在參數(shù)queue隊(duì)列下同步執(zhí)行block

異步函數(shù)

dispatch_async(dispatch_queue_t queue, ^(void)block);

在參數(shù)queue隊(duì)列下異步執(zhí)行block(開(kāi)啟新線(xiàn)程)

時(shí)間

dispatch_time(dispatch_time_t when, int64_t delta);

根據(jù)傳入的時(shí)間(when)和延遲(delta)計(jì)算出一個(gè)未來(lái)的時(shí)間

  • when :
    DISPATCH_TIME_NOW : 現(xiàn)在
    DISPATCH_TIME_FOREVER : 永遠(yuǎn)(別傳這個(gè)參數(shù), 否則該時(shí)間很大)
  • delta : 該參數(shù)接收的是納秒, 可以用一個(gè)宏NSEC_PER_SEC來(lái)進(jìn)行轉(zhuǎn)換, 例如你要延遲3秒, 則為 3 * NSEC_PER_SEC.

延遲執(zhí)行

dispatch_after(dispatch_time_t when, dispatch_queue_t queue, ^(void)block);

有了上述獲取時(shí)間的函數(shù), 則可以直接把時(shí)間傳入, 然后定義該延遲執(zhí)行的block在哪一個(gè)queue隊(duì)列中執(zhí)行.

蘋(píng)果還給我們提供了一個(gè)在主隊(duì)列中延遲執(zhí)行的代碼塊, 如下

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            code to be executed after a specified delay
        });

我們只需要傳入需要延遲的秒數(shù)(delayInSeconds)和執(zhí)行的任務(wù)block就可以直接調(diào)用了, 方便吧~

注意 : 延遲執(zhí)行不是在指定時(shí)間后執(zhí)行任務(wù)處理, 而是在指定時(shí)間后將處理追加到隊(duì)列中, 這個(gè)是要分清楚的

隊(duì)列組

dispatch_group_create();

有時(shí)候我們想要在隊(duì)列中的多個(gè)任務(wù)都處理完畢之后做一些事情, 就能用到這個(gè)Group. 同隊(duì)列一樣, Group在使用完畢也是需要dispatch_release掉的(MRC). 上代碼

group

組異步函數(shù)

dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, ^(void)block);

分發(fā)Group內(nèi)的并發(fā)異步函數(shù)

組通知

dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, ^(void)block)

監(jiān)聽(tīng)group的任務(wù)進(jìn)度, 當(dāng)group內(nèi)的任務(wù)全部完成, 則在queue隊(duì)列中執(zhí)行block.

組等待

dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)

  • timeout : 等待的時(shí)間
    DISPATCH_TIME_NOW : 現(xiàn)在
    DISPATCH_TIME_FOREVER : 永遠(yuǎn)

該函數(shù)會(huì)一直等待組內(nèi)的異步函數(shù)任務(wù)全部執(zhí)行完畢才會(huì)返回. 所以該函數(shù)會(huì)卡住當(dāng)前線(xiàn)程. 若參數(shù)timeout為DISPATCH_TIME_FOREVER, 則只要group內(nèi)的任務(wù)尚未執(zhí)行結(jié)束, 就會(huì)一直等待, 中途不能取消.

柵欄

dispatch_barrier_async(dispatch_queue_t queue, ^(void)block)

在訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)或文件時(shí), 為了提高效率, 讀取操作放在并行隊(duì)列中執(zhí)行. 但是寫(xiě)入操作必須在串行隊(duì)列中執(zhí)行(避免資源搶奪問(wèn)題). 為了避免麻煩, 此時(shí)dispatch_barrier_async函數(shù)作用就出來(lái)了, 在這函數(shù)里進(jìn)行寫(xiě)入操作, 寫(xiě)入操作會(huì)等到所有讀取操作完畢后, 形成一道柵欄, 然后進(jìn)行寫(xiě)入操作, 寫(xiě)入完畢后再把柵欄移除, 同時(shí)開(kāi)放讀取操作. 如圖

dispatch_barrier_async

快速迭代

dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){ 
    // code here
});

執(zhí)行10次代碼, index順序不確定. dispatch_apply會(huì)等待全部處理執(zhí)行結(jié)束才會(huì)返回. 意味著dispatch_apply會(huì)阻塞當(dāng)前線(xiàn)程. 所以dispatch_apply一般用于異步函數(shù)的block中.

一次性代碼

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ 
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線(xiàn)程安全的)
});

該代碼在整個(gè)程序的生命周期中只會(huì)執(zhí)行一次.

掛起和恢復(fù)

dispatch_suspend(queue)

掛起指定的queue隊(duì)列, 對(duì)已經(jīng)執(zhí)行的沒(méi)有影響, 追加到隊(duì)列中尚未執(zhí)行的停止執(zhí)行.

dispatch_resume(queue)

恢復(fù)指定的queue隊(duì)列, 使尚未執(zhí)行的處理繼續(xù)執(zhí)行.

5. GCD的注意點(diǎn)

因?yàn)樵贏RC下, 不需要我們釋放自己創(chuàng)建的隊(duì)列, 所以GCD的注意點(diǎn)就剩下死鎖

死鎖
NSLog(@"111");
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"222");
});
NSLog(@"333");

以上三行代碼將輸出什么?
111
222
333 ?
還是
111
333 ?
其實(shí)都不對(duì), 輸出結(jié)果是
111

為什么? 看下圖

死鎖

毫無(wú)疑問(wèn)會(huì)先輸出111, 然后在當(dāng)前隊(duì)列下調(diào)用dispatch_sync函數(shù), dispatch_sync函數(shù)會(huì)把block追加到當(dāng)前隊(duì)列上, 然后等待block調(diào)用完畢該函數(shù)才會(huì)返回, 不巧的是, block在隊(duì)列的尾端, 而隊(duì)列正在執(zhí)行的是dispatch_sync函數(shù). 現(xiàn)在的情況是, block不執(zhí)行完畢, dispatch_sync函數(shù)就不能返回, dispatch_sync不返回, 就沒(méi)機(jī)會(huì)執(zhí)行block函數(shù). 這種你等我, 我也等你的情況就是死鎖, 后果就是大家都執(zhí)行不了, 當(dāng)前線(xiàn)程卡死在這里.

如何避免死鎖?

不要在當(dāng)前隊(duì)列串行隊(duì)列中使用同步函數(shù), 在隊(duì)列嵌套的情況下也不允許. 如下圖,

隊(duì)列嵌套調(diào)用同步函數(shù)引發(fā)死鎖

大家可以想象, 隊(duì)列1執(zhí)行完NSLog后到隊(duì)列2中執(zhí)行NSLog, 隊(duì)列2執(zhí)行完后又跳回隊(duì)列1中執(zhí)行NSLog, 由于都是同步函數(shù), 所以最內(nèi)層的NSLog("333"); 追加到隊(duì)列1中, 實(shí)際上最外層的dispatch_sync是還沒(méi)返回的, 所以它沒(méi)有執(zhí)行的機(jī)會(huì). 也形成死鎖. 運(yùn)行程序, 果不其然, 打印如下 :
111
222

6. GCD的使用場(chǎng)景

線(xiàn)程間的通信

這是GCD最常用的使用場(chǎng)景了, 如下代碼

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 執(zhí)行耗時(shí)操作
            
    dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主線(xiàn)程作刷新UI等操作
    });
});

為了不阻塞主線(xiàn)程, 我們總是在后臺(tái)線(xiàn)程中發(fā)送網(wǎng)絡(luò)請(qǐng)求, 處理數(shù)據(jù), 然后再回到主線(xiàn)程中刷新UI界面

單例

單例也就是在程序的整個(gè)生命周期中, 該類(lèi)有且僅有一個(gè)實(shí)例對(duì)象, 此時(shí)為了保證只有一個(gè)實(shí)例對(duì)象, 我們這里用到了dispatch_once函數(shù)

static XXTool *_instance;

+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [self allocWithZone:zone];
    });
    
    return _instance;
}

+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    
    return _instance;
}

- (id)copy
{
    return _instance;
}

- (id)mutableCopy
{
    return _instance;
}

因?yàn)閍lloc內(nèi)部會(huì)調(diào)用allWithZone, 所以我們重寫(xiě)allocWithZone方法就行了. 通過(guò)以上代碼可以保證程序只能創(chuàng)建一個(gè)實(shí)例對(duì)象, 并且該實(shí)例對(duì)象永遠(yuǎn)存在程序中.

同步隊(duì)列和鎖

我們知道, 屬性中有atomic和nonatomic屬性

  • atomic : setter方法線(xiàn)程安全, 需要消耗大量的資源
  • nonatomic : setter方法非線(xiàn)程安全, 適合內(nèi)存小的移動(dòng)設(shè)備

為了實(shí)現(xiàn)屬性線(xiàn)程安全, 避免資源搶奪的問(wèn)題, 我們也許會(huì)這樣寫(xiě)

- (NSString *)setMyString:(NSString *)myString
{
    @synchronized(self) {
        _myString = myString;
    }
}

這種方法沒(méi)錯(cuò)是可以達(dá)到該屬性線(xiàn)程安全的需求, 但是試想一下, 如果一個(gè)對(duì)象中有許多個(gè)屬性都需要保證線(xiàn)程安全, 那么就會(huì)在self對(duì)象上頻繁加鎖, 那么兩個(gè)毫無(wú)關(guān)系的setter方法就有可能執(zhí)行一個(gè)setter方法需要等待另一個(gè)setter方法執(zhí)行完畢解鎖之后才能執(zhí)行, 這樣做毫無(wú)必要. 那么你有可能會(huì)說(shuō), 在每個(gè)方法內(nèi)部創(chuàng)建一個(gè)鎖對(duì)象就好啦, 不過(guò)你不覺(jué)得這樣會(huì)浪費(fèi)資源嗎?

那么能不能利用隊(duì)列, 實(shí)現(xiàn)getter方法可以并發(fā)執(zhí)行, 而setter方法串行執(zhí)行并且setter和getter不能并發(fā)執(zhí)行呢??? 沒(méi)錯(cuò), 我們這里用到了dispatch_barrier_async函數(shù).

- (NSString *)myString
{
    __block NSString *localMyString = nil;
    
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        localMyString = self.myString;
    });
    
    return localMyString;
}

- (void)setMyString:(NSString *)myString
{
    dispatch_barrier_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        _myString = myString;
    });
}

這里利用了柵欄塊必須單獨(dú)執(zhí)行, 不能與其他塊并行的特性, 寫(xiě)入操作就必須等當(dāng)前的讀取操作都執(zhí)行完畢, 然后單獨(dú)執(zhí)行寫(xiě)入操作, 等待寫(xiě)入操作執(zhí)行完畢后再繼續(xù)處理讀取.

7. Dispatch Source

它是BSD系內(nèi)核慣有功能kqueue的包裝. kqueue的CPU負(fù)荷非常小, 可以說(shuō)是應(yīng)用程序處理XNU內(nèi)核中發(fā)生的各種事件的方法中最優(yōu)秀的一種.

但是由于Dispatch Source實(shí)在是太少人用了, 所以這里不再介紹. 感興趣的朋友們可以自行Google.

8. 總結(jié)

  • GCD可進(jìn)行線(xiàn)程間通信
  • GCD可以辦到線(xiàn)程安全
  • GCD可用于延遲執(zhí)行
  • GCD需要注意死鎖問(wèn)題(不要在當(dāng)前隊(duì)列調(diào)用同步函數(shù))

想再往深了解并發(fā)編程, 可以看看這篇文章
并發(fā)編程 : API及挑戰(zhàn)


歡迎大家關(guān)注@Jerry4me, 關(guān)注菜鳥(niǎo)成長(zhǎng)_. 我會(huì)不定時(shí)更新一些學(xué)習(xí)心得與文章.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烟具,隨后出現(xiàn)的幾起案子使兔,更是在濱河造成了極大的恐慌默色,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壶笼,死亡現(xiàn)場(chǎng)離奇詭異神僵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)覆劈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)挑豌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人墩崩,你說(shuō)我怎么就攤上這事氓英。” “怎么了鹦筹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵铝阐,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我铐拐,道長(zhǎng)徘键,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任遍蟋,我火速辦了婚禮吹害,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘虚青。我一直安慰自己它呀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纵穿,像睡著了一般下隧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谓媒,一...
    開(kāi)封第一講書(shū)人閱讀 51,182評(píng)論 1 299
  • 那天淆院,我揣著相機(jī)與錄音,去河邊找鬼句惯。 笑死土辩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的抢野。 我是一名探鬼主播脯燃,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蒙保!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起欲主,我...
    開(kāi)封第一講書(shū)人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤邓厕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扁瓢,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體详恼,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年引几,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昧互。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡伟桅,死狀恐怖敞掘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情楣铁,我是刑警寧澤玖雁,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站盖腕,受9級(jí)特大地震影響赫冬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溃列,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一劲厌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧听隐,春花似錦补鼻、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)增淹。三九已至,卻和暖如春乌企,著一層夾襖步出監(jiān)牢的瞬間虑润,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工加酵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拳喻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓猪腕,卻偏偏與公主長(zhǎng)得像冗澈,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子陋葡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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