讀書筆記-《Objective-C高級編程》之Grand Central Dispatch

Grand Central Dispatch

3.1 Grand Central Dispatch(GCD)摘要

3.1.1 什么是GCD

Grand Central Dispatch(GCD)是異步執(zhí)行的任務(wù)之一顶瞒。

3.1.2 多線程編程

在Objective-C的if語句和for語句等控制語句或函數(shù)調(diào)用的情況下抒痒,執(zhí)行命令列的地址會遠離當(dāng)前的位置(位置偏移)友鼻。但是芭梯,由于一個CPU一次只能執(zhí)行一個命令列,不執(zhí)行某處分開的并列的兩個命令露筒,因此通過CPU執(zhí)行的CPU命令列就好比一條無分叉的大道旬牲,其整不會出現(xiàn)分歧猎贴。

通過CPU執(zhí)行的CPU命令列

這里所說的“一個CPU執(zhí)行的CPU命令列為一條無分叉路徑”即為線程。這種無分叉路徑不止一條各聘,存在多條即為“多線程揣非。”在多線程中伦吠,一個CPU核執(zhí)行多條不同路徑上的不同命令妆兑。

在多線程中執(zhí)行CPU命令列

基本上一個CPU核一次能夠執(zhí)行的CPU命令始終為1.

多線程的問題:數(shù)據(jù)競爭、死鎖毛仪、太多線程導(dǎo)致消耗大量內(nèi)存等搁嗓。

多線程可以保證應(yīng)用程序的響應(yīng)性能。應(yīng)用程序啟動時箱靴,通過最先執(zhí)行的線程腺逛,即“主線程”來描繪用戶界面、處理觸摸屏幕的事件等衡怀。如果該主線程中進行長時間的處理棍矛,如AR用畫像的識別或數(shù)據(jù)庫訪問安疗,就會妨礙主線程德執(zhí)行(阻塞)。在OSX和iOS的應(yīng)用程序中够委,會妨礙主線程中被稱為RunLoop的主循環(huán)的執(zhí)行荐类,從而導(dǎo)致不能更新用戶界面,應(yīng)用程序的書面長時間停滯等問題茁帽。

多線程的優(yōu)點

使用多線程玉罐,在執(zhí)行長時間的處理時仍可保證用戶界面的響應(yīng)性能壤巷。

3.2 GCD的API

3.2.1 Dispatch Queue

GCD官方說明:開發(fā)者要做的只是定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue 中监嗜。

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

將Block通過dispatch_async函數(shù)"追加"賦值在變量queue的“Dispatch Queue中”。Dispatch Queue按照追加的順序(先進先出FIFO)執(zhí)行處理

通過Dispatch Queue執(zhí)行處理

在執(zhí)行處理時存在兩種Dispatch Queue激率,一種是等待現(xiàn)在執(zhí)行中處理的Serial Disaptch Queue铁追,另一種是不等待現(xiàn)在執(zhí)行中處理的Concurrent Dispatch Queue季蚂。

Dispatch Queue 的種類 說明
Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束
Concurent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束
Serial Dispatch Queue 和 Concurent Dispatch Queue

并行執(zhí)行的處理數(shù)量取決于當(dāng)前系統(tǒng)的狀態(tài)。即 iOS 和 OSX 基于 Dispatch Queue 中的處理數(shù)琅束、CPU核數(shù)以及CPU負荷等當(dāng)前系統(tǒng)的狀態(tài)來決定 Concurent Dispatch Queue 中并行執(zhí)行的處理數(shù)扭屁。所謂“并行執(zhí)行”,就是使用多個線程執(zhí)行多個處理狰闪。

Serial Dispatch Queue疯搅、 Concurent Dispatch Queue 和線程的關(guān)系

iOS 和 OSX 的核心————XNU內(nèi)核決定應(yīng)當(dāng)使用的線程數(shù),并只生成所需的線程執(zhí)行處理埋泵。另外幔欧,當(dāng)處理結(jié)束,應(yīng)當(dāng)執(zhí)行的處理數(shù)減少時丽声,XNU內(nèi)核會結(jié)束不再需要的內(nèi)核礁蔗。XNU內(nèi)核僅使用 Concurent Dispatch Queue 便可完美地管理并行執(zhí)行多個處理的線程。

3.2.2 dispatch_queue_create

通過dispatch_queue_create函數(shù)可生成 Dispatch Queue雁社。以下源代碼生成了 Serial Dispatch Queue浴井。

    dispatch_queue_t serialQueue = 
      dispatch_queue_create("com.example.gcddemo.serialqueue", NULL);

在說明dispatch_queue_create函數(shù)之前,先講一下關(guān)于 Serial Dispatch Queue 生成個數(shù)的注意事項霉撵。

如前所述磺浙, Concurrent Dispatch Queue 并行執(zhí)行多個追加處理,而 Serial Dispatch Queue 同時只能追加一個處理徒坡。雖然 Serial Dispatch Queue 和 Concurrent Dispatch Queue 受到系統(tǒng)資源的限制撕氧, 但用dispatch_queue_create函數(shù)可生成任意多個 Dispatch Queue。

當(dāng)生成多個 Serial Dispatch Queue 時喇完,各個 Serial Dispatch Queue將并行執(zhí)行伦泥。雖然在一個 Serial Dispatch Queue 中同時只能執(zhí)行一個追加處理,但如果將處理分別追加到4個 Serial Dispatch Queue 中, 各個 Serial Dispatch Queue 執(zhí)行一個不脯,即為同時執(zhí)行四個處理(串行異步)府怯。

多個 Serial Dispatch Queue

如果過多使用多線程,就會消耗大量內(nèi)存防楷,引發(fā)大量的上下文切換牺丙,大幅度降低系統(tǒng)的響應(yīng)性能。

多個 Serial Dispatch Queue 引發(fā)的問題

只在為了避免多線程編程問題之一————多個線程更新想用資源所導(dǎo)致數(shù)據(jù)競爭時使用 Serial Dispatch Queue复局。

Serial Dispatch Queue 的用途

繼續(xù)說明dispatch_queue_create函數(shù)赘被,第一個參數(shù)為生成的 queue 的名稱,推薦使用應(yīng)用程序ID這種逆序全程域名(FQDN肖揣, fully qualified domain name),便于在調(diào)試和日志中定位問題浮入。第二個參數(shù)龙优,生成 Serial Dispatch Queue 時可為 DISPATCH_QUEUE_SERIAL 或 NULL, 生成 Concurent Dispatch Queue 時為DISPATCH_QUEUE_CONCURRENT

iOS6.0后事秀,Dispatch Queue不再需要程序員負責(zé)釋放(dispatch/_release)彤断。

3.2.3 Main Dispatch Queue / Global Dispatch Queue

Main Dispatch Queue 是在主線程中執(zhí)行的 Dispatch Queue。因為主線程只有一個易迹,所以 Main Dispatch Queue 是 Serial Dispatch Queue宰衙。

追加到 Main Dispatch Queue 的處理在主線程的 RunLoop 中執(zhí)行。由于在主線程中執(zhí)行睹欲,因此要將用戶界面的界面更新等一些必須在主線程中執(zhí)行的處理追加到 Main Dispatch Queue 使用供炼。這正好與NSObject類的 performSelectorOnMainThread實例方法這一執(zhí)行的方法相同。

Main Dispatch Queue

Global Dispatch Queue 是所有應(yīng)用程序都能夠使用的 Concurrent Dispatch Queue窘疮。需要時獲取使用即可

Global Dispatch Queue 有4個優(yōu)先級袋哼,但是通過 XNU 內(nèi)核用于 Global Dispatch Queue 的線程并不能保證實時性,因此執(zhí)行優(yōu)先級只是大致的判斷闸衫。

Dispatch Queue的種類

名稱 Dispatch Queue 的種類 說明
Main Dispatch Queue Serial Dispatch Queue 主線程執(zhí)行
Global Dispatch Queue (High Priority) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:高(最高優(yōu)先)
Global Dispatch Queue (Default Priority) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:默認
Global Dispatch Queue (Low Priority) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:低
Global Dispatch Queue (Background Priority) Concurrent Dispatch Queue 執(zhí)行優(yōu)先級:后臺

3.2.4 dispatch_set_target_queue

dispatch_set_target_queue是用來變更生成的 Dispatch Queue 的執(zhí)行優(yōu)先級的涛贯。

    dispatch_set_target_queue(<#dispatch_object_t  _Nonnull object#>, <#dispatch_queue_t  _Nullable queue#>)

指定要變更執(zhí)行優(yōu)先級的 Dispatch Queue 為dispatch_set_target_queue函數(shù)的第一個參數(shù),指定與要使用的執(zhí)行優(yōu)先級相同的 Dispatch Queue 為第二個參數(shù)(目標)蔚出。

將 Dispatch Queue 指定為 dispatch_set_target_queue 函數(shù)的參數(shù)弟翘,不僅可以變更 Dispatch Queue 的執(zhí)行優(yōu)先級,還可以作成 Dispatch Queue 的執(zhí)行階層骄酗。如果多個 Serial Dispatch Queue 中使用dispatch_set_target_queue函數(shù)指定目標為某一個 Serial Dispatch Queue 稀余,那么原本應(yīng)并行執(zhí)行的多個 Serial Dispatch Queue ,在目標 Serial Dispatch Queue 上只能同時執(zhí)行一個處理酥筝。

Dispatch Queue 的執(zhí)行層次

在必須將不可并行執(zhí)行的處理追加到多個 Serial Dispatch Queue 中時滚躯,如果使用dispatch_set_target_queue函數(shù)將目標指定為某一個 Serial Dispatch Queue,即可防止處理并行執(zhí)行。

3.2.5 dispatch_after

dispatch_after函數(shù)并不是在指定時間后執(zhí)行處理掸掏,而只是在指定時間追加處理到 Dispatch Queue茁影。

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"waited after at least three seconds");
    });

因為 Main Dispatch Queue 在主線程的 RunLoop 執(zhí)行,所以在比如每隔1/60秒執(zhí)行的 RunLoop 中丧凤,Block 最快在3秒后募闲,最慢在3秒+1/60秒后執(zhí)行,并且在 Main Dispatch Queue 有大量處理追加或主線程處理本身有延遲時愿待,這個時間會更長浩螺。

第一個參數(shù)是指定時間用的dispatch_time_t類型的值。該值使用dispatch_time函數(shù)(計算相對時間)或dispatch_walltime函數(shù)(計算絕對時間)仍侥。

3.2.6 Dispatch Group

當(dāng)追加到的 Dispatch Queue 中的多個處理全部結(jié)束后想執(zhí)行結(jié)束處理要出,使用 Dispatch Group。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, queue, ^{
        NSLog(@"blk0");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"blk1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"blk2");
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"done");
    });

執(zhí)行結(jié)果

    blk0
    blk1
    blk2
    done

在 Dispatch Group 中也可以使用 dispatch_group_wait函數(shù)僅等待全部處理執(zhí)行結(jié)束农渊。

    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
    long result = dispatch_group_wait(group, time);
    if (result == 0) {
        // 屬于 Dispatch Group 的全部處理執(zhí)行結(jié)束
    } else {
        // 屬于 Dispatch Group 的某一個處理還在執(zhí)行
    }

當(dāng)dispatch_group_wait函數(shù)返回的等待時間為DISPATCH_TIME_FOREVER時患蹂,意味著永久等待,屬于 Dispatch Group 的處理必定全部執(zhí)行結(jié)束砸紊,因此返回值恒為0传于。

需要注意的是,一旦調(diào)用dispatch_group_wait函數(shù)醉顽,該函數(shù)就處于調(diào)用的狀態(tài)而不返回沼溜。即執(zhí)行dispatch_group_wait函數(shù)的現(xiàn)在的線程(當(dāng)前線程)停止。在經(jīng)過dispatch_group_wait函數(shù)中指定的時間或?qū)儆谥付?Dispatch Group 的處理全部執(zhí)行結(jié)束之前游添,執(zhí)行該函數(shù)的線程停止系草。

指定DISPATCH_TIME_NOW,則不用任何等待即可判定屬于 Dispatch Group 的處理是否執(zhí)行結(jié)束否淤。

3.2.7 dispatch_barrier_async

    dispatch_queue_t queue = dispatch_queue_create("com.example.gcddemo.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 1");
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 2");
    });
    dispatch_barrier_async(dataQueue, ^{
        NSLog(@"write data 1");
    });
    dispatch_async(dataQueue, ^{
        NSLog(@"read data 3");
    });

dispatch_barrier_async函數(shù)會等待之前追加到 Dispatch Queue 中的處理全部執(zhí)行結(jié)束后悄但,再將dispatch_barrier_async函數(shù)指定的處理追加到 Dispatch Queue 中,然后在dispatch_barrier_async函數(shù)追加的處理執(zhí)行完畢后石抡,繼續(xù)執(zhí)行dispatch_barrier_async函數(shù)之后追加到 Dispatch Queue 的處理檐嚣。

dispatch_barrier_async函數(shù)的處理流程

使用 Concurrent Dispatch Queue 和 dispatch_barrier_async函數(shù)可實現(xiàn)搞笑的數(shù)據(jù)庫訪問和文件訪問。

3.2.8 dispatch_sync

dispatch_async(異步)啰扛,指定的 Block ”非同步“地追加到指定的 Dispatch Queue 中嚎京,dispatch_async函數(shù)不做任何等待。

Dispatch_async 函數(shù)的處理流程

dispatch_sync(同步)隐解,將指定的Block”同步“追加到指定的 Dispatch Queue 中鞍帝,在追加Block結(jié)束之前,dispatch_sync函數(shù)會一直等待煞茫。

Dispatch_sync 函數(shù)的處理流程

使用dispatch_sync函數(shù)需要注意死鎖問題帕涌。

    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"123");
    });

該源碼在主線程中執(zhí)行指定的 Block摄凡,并等待其執(zhí)行結(jié)束,而其實在主線程中正在執(zhí)行這些源代碼蚓曼,所以無法追加到主線程的 Block亲澡。

3.2.9 dispatch_apply

da函數(shù)是ds函數(shù)和 dg 的關(guān)聯(lián)API。該函數(shù)按指定的次數(shù)將指定的Block追加到指定的 Dispatch Queue 中纫版,并等待全部處理執(zhí)行結(jié)束床绪。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zu", index);
    });
    NSLog(@"done");

執(zhí)行結(jié)果

    0
    1
    3
    2
    5
    4
    6
    7
    8
    9
    done

因為在 Global Dispatch Queue 中執(zhí)行處理,所以各個處理的執(zhí)行時間不定其弊。但是輸出結(jié)果最后的done必定在最后的位置上癞己。這是因為da函數(shù)會等待全部處理執(zhí)行結(jié)束。

由于da函數(shù)與ds函數(shù)相同梭伐,會等待處理執(zhí)行結(jié)束痹雅,因此推薦在dispatch_async函數(shù)中非同步地執(zhí)行da函數(shù)。

對比for循環(huán)中使用dispatch_async函數(shù)糊识,并行的機制比串行的機制更安全练慕、快速。

3.2.10 dispatch_suspend/dispatch_resume

當(dāng)追加大量處理到 Dispatch Queue 時,在追加處理的過程中,有時希望不執(zhí)行已追加的處理技掏。這種情況可以使用dispatch_suspend函數(shù)掛起指定的 Dispatch Queue,當(dāng)可以執(zhí)行時再使用dispatch_resume函數(shù)恢復(fù)指定的 Dispatch Queue项鬼。

dispatch_suspend函數(shù)不會立即停止正在執(zhí)行的 Block哑梳,而是在當(dāng)前 Block 執(zhí)行完成后,暫停后續(xù)的 Block 執(zhí)行绘盟。

3.2.11 Dispatch Semaphore

當(dāng)并行執(zhí)行的處理更新數(shù)據(jù)時鸠真,會產(chǎn)生數(shù)據(jù)不一致的情況,有時應(yīng)用程序還會異常結(jié)束龄毡。雖然使用 Serial Dispatch Queue 和dispatch_barrier_async函數(shù)可避免這類問題吠卷,但有必要進行更細粒度的排他控制,這時可以使用 Dispatch Semaphore沦零。

Dispatch Semaphore 是持有計數(shù)的信號祭隔,該計數(shù)是多線程編程中的計數(shù)類型信號。所謂信號路操,類似于過馬路時常用的手旗疾渴,可以通過時舉起手旗,不可通過時放下手旗屯仗。而在 Dispatch Semaphore 中搞坝,使用計數(shù)來實現(xiàn)該功能。計數(shù)為0時等待魁袜,計數(shù)為1或大于1時桩撮,減去1而不等待敦第。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
   /*
     生成 Dispatch Semaphore
     Dispatch Semaphore的計數(shù)初始值設(shè)定為“1”
     保證可訪問NSMutableArray類對象的線程同時只有一個
    */
   dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
   NSMutableArray *array = [NSMutableArray array];
   
   for (int i = 0; i < 100000; i++) {
       dispatch_async(queue, ^{
           /*
             等待 Dispatch Semaphore
             一直等待,直到 Dispatch Semaphore 的計數(shù)值達到大于等于1
            */
           dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
           
           /*
             由于 Dispatch Semaphore 的計數(shù)值達到大于等于1
             所以將 Dispatch Semaphore 的計數(shù)值減去1
             dispatch_semaphore_wait函數(shù)執(zhí)行返回店量。
             即執(zhí)行到此時的 Dispatch Semaphore 的計數(shù)值恒為0
             由于可訪問NSMutableArray類對象的線程只有1個
             因此可安全地進行更新
            */
           [array addObject:[NSNumber numberWithInt:i]];
           
           /*
             排他控制處理結(jié)束
             所以通過dispatch_semaphore_signal函數(shù)將
             Dispatch Semaphore 的計數(shù)值加1芜果。
             如果有通過dispatch_semaphore_wait函數(shù)
             等待 Dispatch Semaphore 的計數(shù)值增加的線程
             就由最先等待的線程執(zhí)行。
            */
           dispatch_semaphore_signal(semaphore);
       });
   }
   NSLog(@"done");

3.2.12 dispatch_once

dispatch_once函數(shù)式保證在應(yīng)用程序執(zhí)行中只執(zhí)行一次指定處理的API垫桂。多線程環(huán)境下也可保證百分百安全师幕。

    static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       // code to be executed once
   });

3.2.13 Dispatch I/O

在讀取較大文件時,如果將文件分成合適的大小并使用 Global Dispatch Queue 并列讀取的話能提高效率∥芴玻現(xiàn)今的輸入/輸出硬件已經(jīng)可以做到一次使用多個線程更快地并列讀取了霹粥。能實現(xiàn)這一功能的就是 Dispatch I/O 和 Dispatch Data。

通過Dispatch I/O讀寫文件時疼鸟,使用 Global Dispatch Queue 將1個文件按某個大小 read/write后控。

   dispatch_sync(queue, ^{/* 讀取     0 ~  8191 字節(jié)*/});
   dispatch_sync(queue, ^{/* 讀取  8192 ~ 16383 字節(jié)*/});
   dispatch_sync(queue, ^{/* 讀取 16384 ~ 24575 字節(jié)*/});
   dispatch_sync(queue, ^{/* 讀取 24576 ~ 32767 字節(jié)*/});

像上面這樣,將文件分割為一塊一塊地進行讀取處理空镜。

以下為蘋果中使用 Dispatch I/O 和 Dispatch Data 的示例浩淘。

   pipe_q = dispatch_queue_create("PipeQ",NULL);
   pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM,fd,pipe_q,^(int err){
      close(fd);
   });
   
   *out_fd = fdpair[i];
   
   dispatch_io_set_low_water(pipe_channel,SIZE_MAX);
   
   dispatch_io_read(pipe_channel,0,SIZE_MAX,pipe_q, ^(bool done,dispatch_data_t pipe data,int err){
      if(err == 0)
        {
          size_t len = dispatch_data_get_size(pipe data);
          if(len > 0)
          {
             const char *bytes = NULL;
             char *encoded;
   
             dispatch_data_t md = dispatch_data_create_map(pipe data,(const void **)&bytes,&len);
             asl_set((aslmsg)merged_msg,ASL_KEY_AUX_DATA,encoded);
             free(encoded);
             _asl_send_message(NULL,merged_msg,-1,NULL);
             asl_msg_release(merged_msg);
             dispatch_release(md);
          }
         }
   
         if(done)
         {
            dispatch_semaphore_signal(sem);
            dispatch_release(pipe_channel);
            dispatch_release(pipe_q);
         }
   });

dispatch_io_create函數(shù)生成 Dispatch I/O,并指定發(fā)生錯誤時用來執(zhí)行處理的 Block吴攒,以及執(zhí)行該 Block的 Dispatch Queue张抄。dispatch_io_set_low_water函數(shù)設(shè)定一次讀取的大小(分割大型菡)署惯,dispatch_io_read函數(shù)使用 Global Dispatch Queue 開始并列讀取。每當(dāng)各個分割的文件快讀取結(jié)束時镣隶,將含有文件塊數(shù)據(jù)的 Dispatch Data 傳遞給dispatch_io_read函數(shù)指定的讀取結(jié)束時回調(diào)用的 Block极谊。回調(diào)用的 Block 分析傳遞過來的 Dispatch Data 并進行結(jié)合處理安岂。
如果想提高文件讀取速度轻猖,可以嘗試使用Dispatch I/O。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末域那,一起剝皮案震驚了整個濱河市咙边,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌次员,老刑警劉巖样眠,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翠肘,居然都是意外死亡檐束,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門束倍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來被丧,“玉大人盟戏,你說我怎么就攤上這事∩穑” “怎么了柿究?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長黄选。 經(jīng)常有香客問我蝇摸,道長,這世上最難降的妖魔是什么办陷? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任貌夕,我火速辦了婚禮,結(jié)果婚禮上民镜,老公的妹妹穿的比我還像新娘啡专。我一直安慰自己,他們只是感情好制圈,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布们童。 她就那樣靜靜地躺著,像睡著了一般鲸鹦。 火紅的嫁衣襯著肌膚如雪慧库。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天馋嗜,我揣著相機與錄音完沪,去河邊找鬼。 笑死嵌戈,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的听皿。 我是一名探鬼主播熟呛,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尉姨!你這毒婦竟也來了庵朝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤又厉,失蹤者是張志新(化名)和其女友劉穎九府,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體覆致,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡侄旬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了煌妈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片儡羔。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡宣羊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汰蜘,到底是詐尸還是另有隱情仇冯,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布族操,位于F島的核電站苛坚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏色难。R本人自食惡果不足惜泼舱,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望莱预。 院中可真熱鬧柠掂,春花似錦、人聲如沸依沮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽危喉。三九已至宋渔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間辜限,已是汗流浹背皇拣。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留薄嫡,地道東北人氧急。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像毫深,于是被迫代替她去往敵國和親吩坝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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