GCD使用三部曲之:基本用法

GCD

什么是GCD?


GCD全稱為Grand Central Dispatch降宅,是libdispatch的市場名稱,而libdispatch是Apple的一個庫早敬,其為并發(fā)代碼在iOS和OS X的多核硬件上執(zhí)行提供支持蜗巧。確切地說GCD是一套低層級的C API,通過 GCD牙捉,開發(fā)者只需要向隊列中添加一段代碼塊(block或C函數(shù)指針),而不需要直接和線程打交道敬飒。GCD在后端管理著一個線程池邪铲,它不僅決定著你的代碼塊將在哪個線程被執(zhí)行,還根據(jù)可用的系統(tǒng)資源對這些線程進行管理无拗。這樣通過GCD來管理線程带到,從而解決線程被創(chuàng)建的問題。

GCD的優(yōu)勢


  • 易用: GCD 提供一個易于使用的并發(fā)模型而不僅僅只是鎖和線程英染,以幫助我們避開并發(fā)陷阱,而且因為基于block揽惹,它能極為簡單得在不同代碼作用域之間傳遞上下文。
  • 靈活: GCD 具有在常見模式上(比如鎖四康、單例)搪搏,用更高性能的方法優(yōu)化代碼,而且GCD能提供更多的控制權(quán)力以及大量的底層函數(shù)闪金。
  • 性能: GCD能自動根據(jù)系統(tǒng)負載來增減線程數(shù)量疯溺,這就減少了上下文切換以及增加了計算效率。

GCD相關(guān)概念


如果要深入了解GCD毕泌,還有一些概念是需要知道的喝检。

  • **Dispatch Objects **
    盡管GCD是純C語言的嗅辣,但它被組建成面向?qū)ο蟮娘L格撼泛。GCD對象被稱為dispatch object, 所有的dispatch objects都是OC對象.,就如其他OC對象一樣澡谭,當開啟了ARC(automatic reference counting)時,dispatch objects的retain和release都會自動執(zhí)行愿题。而如果是MRC的話,dispatch objects會使用dispatch_retain和dispatch_release這兩個方法來控制引用計數(shù)蛙奖。

  • Serial & Concurrent
    串行任務就是每次只有一個任務被執(zhí)行潘酗,并發(fā)任務就是在同一時間可以有多個任務被執(zhí)行。

  • Synchronous & Asynchronous
    同步函數(shù)意思是在完成了它預定的任務后才返回雁仲,在任務執(zhí)行時會阻塞當前線程仔夺。而異步函數(shù)則是任務會完成但不會等它完成,所以異步函數(shù)不會阻塞當前線程攒砖,會繼續(xù)去執(zhí)行下一個函數(shù)缸兔。

  • Concurrency & Parallelism
    并發(fā)的意思就是同時運行多個任務日裙。這些任務可能是以在單核 CPU 上以分時(時間共享)的形式同時運行,也可能是在多核 CPU 上以真正的并行方式來運行惰蜜。然后為了使單核設(shè)備也能實現(xiàn)這一點昂拂,并發(fā)任務必須先運行一個線程,執(zhí)行一個上下文切換抛猖,然后運行另一個線程或進程格侯。并行則是真正意思上的多任務同時運行。

  • Context Switch
    Context Switch即上下文切換财著,一個上下文切換指當你在單個進程里切換執(zhí)行不同的線程時存儲與恢復執(zhí)行狀態(tài)的過程联四。這個過程在編寫多任務應用時很普遍,但會帶來一些額外的開銷瓢宦。

  • Dispatch Queues
    GCD dispatch queues是一個強大的執(zhí)行多任務的工具碎连。Dispatch queue是一個對象,它可以接受任務驮履,并將任務以先進先出(FIFO)的順序來執(zhí)行鱼辙。Dispatch queue可以并發(fā)的或串行的執(zhí)行任意一個代碼塊,而且并發(fā)任務會像NSOperationQueue那樣基于系統(tǒng)負載來合適地并發(fā)進行玫镐,串行隊列同一時間則只執(zhí)行單一任務倒戏。Dispatch queues內(nèi)部使用的是線程,GCD 管理這些線程恐似,并且使用Dispatch queues的時候杜跷,我們都不需要自己創(chuàng)建線程。Dispatch queues相對于和線程直接通信的代碼優(yōu)勢是:Dispatch queues使用起來特別方便矫夷,執(zhí)行任務更加有效率葛闷。

  • Queue Types
    GCD有三種隊列類型:

類型 描述
Serial 串行隊列將任務以先進先出(FIFO)的順序來執(zhí)行,所以串行隊列經(jīng)常用來做訪問某些特定資源的同步處理双藕。你可以也根據(jù)需要創(chuàng)建多個隊列淑趾,而這些隊列相對其他隊列都是并發(fā)執(zhí)行的。換句話說忧陪,如果你創(chuàng)建了4個串行隊列扣泊,每一個隊列在同一時間都只執(zhí)行一個任務,對這四個任務來說嘶摊,他們是相互獨立且并發(fā)執(zhí)行的延蟹。如果需要創(chuàng)建串行隊列,一般用dispatch_queue_create這個方法來實現(xiàn)叶堆。
Concurrent 并發(fā)隊列雖然是能同時執(zhí)行多個任務阱飘,但這些任務仍然是按照先到先執(zhí)行(FIFO)的順序來執(zhí)行的。并發(fā)隊列會基于系統(tǒng)負載來合適地選擇并發(fā)執(zhí)行這些任務。在iOS5之前沥匈,并發(fā)隊列一般指的就是全局隊列(Global queue)果录,進程中存在四個全局隊列:高、中(默認)咐熙、低弱恒、后臺四個優(yōu)先級隊列,可以調(diào)用dispatch_get_global_queue函數(shù)傳入優(yōu)先級來訪問隊列棋恼。而在iOS5之后返弹,我們也可以用dispatch_queue_create,并指定隊列類型DISPATCH_QUEUE_CONCURRENT爪飘,來自己創(chuàng)建一個并發(fā)隊列义起。
Main dispatch queue 與主線程功能相同。實際上师崎,提交至main queue的任務會在主線程中執(zhí)行默终。main queue可以調(diào)用dispatch_get_main_queue()來獲得。因為main queue是與主線程相關(guān)的犁罩,所以這是一個串行隊列齐蔽。和其它串行隊列一樣,這個隊列中的任務一次只能執(zhí)行一個床估。它能保證所有的任務都在主線程執(zhí)行含滴,而主線程是唯一可用于更新 UI 的線程。

Getting Started!


好了丐巫,在了解了上述概念后谈况,我們可以正式投入GCD的懷抱了!

創(chuàng)建和管理隊列

當你決定添加一些任務到隊列中時递胧,你需要決定該用那種類型的隊列碑韵,并且抉擇該如何使用他們。Dispatch queues可以串行或并發(fā)地執(zhí)行這些任務缎脾,當你腦海里有一個大概的思路去如何使用隊列時祝闻,你可以額快速地設(shè)置好隊列的屬性。接下來的部分赊锚,本文將告訴大家如何創(chuàng)建隊列和怎樣設(shè)置隊列的屬性治筒。

  1. 獲取一個全局隊列
    當我們需要同時執(zhí)行多個任務時屉栓,并發(fā)隊列是非常有用的舷蒲。并發(fā)隊列其實仍然還是一個隊列,它保留了隊列中的任務按先進先出(FIFO)的順序執(zhí)行的特點友多。同時牲平,一個并發(fā)隊列可以移除t它多余的任務,甚至這些任務之前還有未完成的任務域滥。一個并發(fā)隊列中實際執(zhí)行的任務數(shù)是由很多因素決定的纵柿,比如系統(tǒng)的內(nèi)核數(shù)蜈抓,其他串行隊列中任務的優(yōu)先級,以及其他進程的工作狀態(tài)昂儒。

系統(tǒng)為每個程序提供了四種全局隊列沟使,這些隊列中僅僅通過優(yōu)先級加以區(qū)別,這四種類型分別是高渊跋、中(默認)腊嗡、低、后臺拾酝。因為這些隊列是全局的燕少,所以大家不能直接創(chuàng)建它們,取而代之的是我們可以通過dispatch_get_global_queue這個方法來調(diào)用它們蒿囤。

代碼示例:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   //全局隊列的四種類型
   DISPATCH_QUEUE_PRIORITY_HIGH
   DISPATCH_QUEUE_PRIORITY_DEFAULT
   DISPATCH_QUEUE_PRIORITY_LOW
   DISPATCH_QUEUE_PRIORITY_BACKGROUND

正如大家所看到的客们,因為存在隊列的優(yōu)先級,所以那些在高優(yōu)先級隊列中的任務會比在默認或低優(yōu)先級隊列中的任務要先執(zhí)行材诽,而默認級別隊列的優(yōu)先級又高于低優(yōu)先級隊列底挫。注意,這里有一個比較特殊的級別容易被忽視脸侥,DISPATCH_QUEUE_PRIORITY_BACKGROUND凄敢。被設(shè)置成后臺級別的隊列,它會等待所有比它級別高的隊列中的任務執(zhí)行完或CPU空閑的時候才會執(zhí)行自己的任務湿痢。例如磁盤的讀寫操作非常耗時涝缝,如果我們不需要立即獲取到磁盤的數(shù)據(jù),我們可以把讀寫任務放到后臺隊列中譬重,這樣讀寫任務只會在恰當?shù)臅r候去執(zhí)行而不會影響需要更改優(yōu)先級的其他任務拒逮,整個程序也會更加有效率。

Note: 盡管dispatch queues是引用計數(shù)對象臀规,但是我們不需要用retain和release來管理全局的并發(fā)隊列滩援。因為全局隊列對于程序來說是全局的,retain和release會被全局隊列忽略塔嬉。所以玩徊,我們不需要存儲這些隊列的引用數(shù),僅僅只需要在任何要使用它們的地方谨究,調(diào)用dispatch_get_global_queue這個方法即可恩袱。

  1. 創(chuàng)建串行隊列&并發(fā)隊列
    當我們需要某些任務以指定的順序去執(zhí)行時,串行隊列是一個非常好的選擇胶哲。一個串行隊列在同一時間里只會執(zhí)行一個任務畔塔,而且每次都只會從隊列的頭部把任務取出來執(zhí)行。正因為如此,我們可以用串行隊列來替代鎖的操作澈吨,比如數(shù)據(jù)資源的同步或修改數(shù)據(jù)結(jié)構(gòu)時把敢。和鎖不同的是,串行隊列能保證任務都是在可預見的順序里執(zhí)行谅辣,而且一旦我們在一個串行隊列里異步提交了任務修赞,隊列就能永遠不發(fā)生死鎖。怎么樣桑阶,是不是很棒榔组,不過不像并發(fā)隊列,這些串行隊列是需要我們自己創(chuàng)建和管理的联逻。

我們還可以在程序里創(chuàng)建任意數(shù)量的隊列搓扯,不過值得注意的是,我們要盡量避免創(chuàng)建大量的串行隊列而目的僅僅是為了同時執(zhí)行隊列中的這些任務包归。雖然GCD 通過創(chuàng)建所謂的線程池來大致匹配 CPU 內(nèi)核數(shù)量锨推,但是線程的創(chuàng)建并不是無代價的。每個線程都需要占用內(nèi)存和內(nèi)核資源公壤。所以如果需要創(chuàng)建大量的并發(fā)任務换可,我們只需要把這些任務放到并發(fā)隊列中即可。

代碼示例:

//dispatch_queue_t
   //dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

  //串行隊列
   dispatch_queue_t serialQueue;
   serialQueue = dispatch_queue_create("com.example.SerialQueue", NULL);

  //并發(fā)隊列
  dispatch_queue_t concurrentQueue;
   concurrentQueue = dispatch_queue_create("com.example.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

NOTE: dispatch_queue_attr_t設(shè)置成NULL的時候默認代表串行厦幅。

  1. 獲取Main Queue
    獲取主隊列的方法很簡單沾鳄,如下所示:
dispatch_queue_t mainQueue;
mainQueue = dispatch_get_main_queue();
  1. 創(chuàng)建隊列的自定義上下文
    所有的dispatch objects(包括dispatch queues)允許我們關(guān)聯(lián)自定義的上下文。我們可以通過使用** dispatch_set_contextdispatch_get_context**這兩個方法确憨,來為objects設(shè)定和獲取這些上下文數(shù)據(jù)译荞。因為系統(tǒng)不會使用我們自定義的數(shù)據(jù),所以我們需要在適當?shù)臅r候生成和銷毀這些數(shù)據(jù)休弃。對于隊列吞歼,我們可以使用上下文來為一個OC對象或其他數(shù)據(jù)結(jié)構(gòu)存儲一個指針,以此來作為某個隊列的唯一標識塔猾。我們可以在隊列銷毀前并在隊列最后執(zhí)行的方法中去銷毀上下文數(shù)據(jù)篙骡。

代碼示例:

void myFinalizerFunction(void *context)
 {
     MyDataContext* theData = (MyDataContext*)context;
     // 清除這個數(shù)據(jù)的內(nèi)容
     myCleanUpDataContextFunction(theData);
     // 釋放數(shù)據(jù).
     free(theData);
  }

 dispatch_queue_t createMyQueue()
 {
     MyDataContext*  data = (MyDataContext*) malloc(sizeof(MyDataContext));
     myInitializeDataContextFunction(data);
     // 創(chuàng)建隊列并設(shè)置上下文.
     dispatch_queue_t serialQueue =
 dispatch_queue_create("com.example.CriticalTaskQueue", NULL);
     if (serialQueue)
     {
        dispatch_set_context(serialQueue, data);
        dispatch_set_finalizer_f(serialQueue, &myFinalizerFunction);
     }
     return serialQueue;
 }

添加任務到隊列

GCD有兩種方式來把任務添加到隊列中:異步和同步。一般情況下丈甸,使用dispatch_asyncdispatch_async_f來執(zhí)行異步操作糯俗,是比同步操作更好的選擇。當我們添加一個block對象或C函數(shù)到一個隊列中后就立即返回了睦擂,任務會在之后由 GCD 決定執(zhí)行得湘,以及任務什么時候執(zhí)行完我們是無法知道確定的。這樣的好處是祈匙,如果我們需要在后臺執(zhí)行一個基于網(wǎng)絡或 CPU 緊張的任務時就使用異步方法 忽刽,這樣就不會阻塞當前線程。

盡管一般情況下夺欲,我們會優(yōu)先選擇異步操作跪帝,但是在某些情況下,我們還是需要任務同步來執(zhí)行些阅。比如需要用同步操作來防止資源競爭或其他同步問題伞剑。這時,我們可以用** dispatch_syncdispatch_sync_f**方法來把任務添加到隊列中市埋,這樣被添加的任務會阻塞當前線程黎泣,直到這些任務執(zhí)行完。

代碼示例:

//代碼示例:
//異步執(zhí)行
dispatch_queue_t myCustomQueue;
myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);
dispatch_async(myCustomQueue, ^{
    NSLog("Do some work here.");
});
//同步執(zhí)行
dispatch_sync(myCustomQueue, ^{
    NSLog("Do some more work here.");
});

NOTE:dispatch_async在不同隊列類型執(zhí)行的情況

  • 自定義串行隊列:當你想串行執(zhí)行后臺任務并追蹤它時就是一個好選擇缤谎。這消除了資源爭用抒倚,因為你知道一次只有一個任務在執(zhí)行。
  • 主隊列:這是在一個并發(fā)隊列上完成任務后更新 UI 的一般選擇坷澡。
  • 并發(fā)隊列:這是在后臺執(zhí)行非 UI 工作的一般選擇

任務執(zhí)行完后添加一個完成塊(Completion Block)

通常來說托呕,我們把任務添加到隊列后,一旦任務執(zhí)行完频敛,我們希望能得到通知并及時處理任務完成的結(jié)果项郊。安裝傳統(tǒng)的異步開發(fā)流程,我們可以使用回調(diào)機制斟赚,或者在隊列中使用完成塊(Completion Block)着降。

一個Completion Block是在原任務完成后,我們給隊列添加的一個代碼塊拗军∪味矗回調(diào)代碼的經(jīng)典做法一般是在任務開始時,把completion block當成一個參數(shù)发侵。需要我們做的只是把一個指定的block或函數(shù)侈咕,在指定的隊列完成時,提交給這個隊列即可器紧。

下面是在一個計算平均值的函數(shù)耀销,其利用了block方法來作為運算結(jié)果的回調(diào)。這個函數(shù)的最后參數(shù)queue铲汪、block熊尉,指定了一個queue和一個block,其在計算完數(shù)值結(jié)果后會把結(jié)果值傳給這個block掌腰,然后再把block分發(fā)到這個隊列(queue)中狰住。注意,為了避免queue被提前釋放掉了齿梁,我們可以在函數(shù)執(zhí)行開始階段為隊列retain催植,然后在completion block完成后再release隊列肮蛹。

代碼示例:

void average_async(int *data, size_t len,
   dispatch_queue_t queue, void (^block)(int))
{
   // Retain the queue 以此確保在completion block
   // 完成前不會被釋放掉
   dispatch_retain(queue);
   // Do the work on the default concurrent queue and then
   // call the user-provided block with the results.
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
      int avg = average(data, len);
      dispatch_async(queue, ^{ block(avg);});
      // Release the queue
      dispatch_release(queue);
   });
}

并發(fā)執(zhí)行迭代循環(huán)

在開發(fā)中,并發(fā)隊列能很好地提高效率创南,特別是當我們需要執(zhí)行一個數(shù)據(jù)龐大的循環(huán)操作時伦忠。打個比方來說吧,我們需要執(zhí)行一個for循環(huán)稿辙,每一次循環(huán)操作如下:

for (i = 0; i < count; i++) {
   NSLog("%d",i);
}

GCD提供了一個簡化方法叫做dispatch_apply昆码,當我們把這個方法放到并發(fā)隊列中執(zhí)行時,這個函數(shù)會調(diào)用單一block多次邻储,并平行運算赋咽,然后等待所有運算結(jié)束。

代碼示例:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(count, queue, ^(size_t i) {
   NSLog("%d",i);
});

怎么樣吨娜,是不是很棒脓匿,但是需要異步怎么辦?dispatch_apply函數(shù)是沒有異步版本的宦赠。解決的方法是只要用dispatch_async函數(shù)將所有代碼推到后臺就行了亦镶。

代碼示例:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(queue, ^{
    dispatch_apply(count, queue, ^(size_t i) {
       NSLog("%d",i);
    });
});

掛起和恢復隊列

有時候,我們不想讓隊列中的某些任務馬上執(zhí)行袱瓮,這時我們可以通過掛起操作來阻止一個隊列中將要執(zhí)行的任務缤骨。當需要掛起隊列時,使用dispatch_suspend方法尺借;恢復隊列時绊起,使用dispatch_resume方法。調(diào)用dispatch_suspend會增加隊列掛起的引用計數(shù)燎斩,而調(diào)用dispatch_resume則會減少引用計數(shù)虱歪,當引用計數(shù)大于0時,隊列會保持掛起狀態(tài)栅表。因此笋鄙,這隊列的掛起和恢復中,我們需要小心使用以避免引用計數(shù)計算錯誤的出現(xiàn)怪瓶。

代碼示例:

dispatch_queue_t myQueue;
myQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);
//掛起隊列
dispatch_suspend(myQueue);
//恢復隊列
dispatch_resume(myQueue);

NOTE:執(zhí)行掛起操作不會對已經(jīng)開始執(zhí)行的任務起作用萧落,它僅僅只會阻止將要進行但是還未開始的任務。

使用Dispatch Semaphores

信號量的作用是控制多個任務對有限數(shù)量資源的訪問洗贰。一個dispatch semaphore就像一個普通信號的例外找岖。當資源可用時,獲取dispatch semaphore的時間比獲取傳統(tǒng)的系統(tǒng)信號量要更少敛滋。這是因為GCD不調(diào)用這個特殊情況下的內(nèi)核许布。唯一的一次需要在內(nèi)核中調(diào)用的情況是,當資源不可用且系統(tǒng)需要在停止你的線程直到獲取信號绎晃。舉例來說更容易理解蜜唾,如果你創(chuàng)建了一個有著兩個資源的信號量杂曲,那同時最多只能有兩個線程可以訪問臨界區(qū)。其他想使用資源的線程必須在FIFO隊列里等待袁余。

常用的dispatch semaphore的語法:

  1. 當創(chuàng)建信號量(使用dispatch_semaphore_create方法)擎勘,我們可以指定一個正整數(shù),表示可用資源的數(shù)量泌霍。

  2. 在每一個任務里货抄,調(diào)用dispatch_semaphore_wait來等待信號量述召。

  3. 當?shù)却{(diào)用返回時朱转,獲取資源并做自己的工作。

  4. 當我們用到資源后积暖,釋放掉它藤为,然后通過調(diào)用dispatch_semaphore_signal方法來發(fā)出信號。

每一個應用都提供了有限的文件描述符來使用夺刑,如果我們需要處理一大堆的文件時缅疟,我們不想在運行文件描述符的時候同時打開很多文件。取而代之的是遍愿,我們可以用信號量來限制同一時間里文件描述符的數(shù)量存淫。下面就是為了實現(xiàn)此需求的簡單代碼:

// 創(chuàng)建一個信號量
dispatch_semaphore_t fd_sema = dispatch_semaphore_create(getdtablesize() / 2);
// 等待一個空閑的文件描述符
dispatch_semaphore_wait(fd_sema, DISPATCH_TIME_FOREVER);
fd = open("/etc/services", O_RDONLY);
// 當完成時,釋放掉文件描述符
close(fd);
dispatch_semaphore_signal(fd_sema);

Dispatch Groups的使用

Dispatch groups是阻塞線程直到一個或多個任務完成的一種方式沼填。在那些需要等待任務完成才能執(zhí)行某個處理的時候桅咆,你可以使用這個方法。Dispatch Group會在整個組的任務都完成時通知你坞笙,這些任務可以是同步的岩饼,也可以是異步的,即便在不同的隊列也行薛夜。而且在整個組的任務都完成時籍茧,Dispatch Group可以用同步的或者異步的方式通知你。當group中所有的任務都完成時梯澜,GCD 提供了兩種通知方式寞冯。

  1. dispatch_group_wait。它會阻塞當前線程晚伙,直到組里面所有的任務都完成或者等到某個超時發(fā)生简十。

代碼示例:

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, ^{
  // 一些異步操作
});
//如果在所有任務完成前超時了,該函數(shù)會返回一個非零值撬腾。
//你可以對此返回值做條件判斷以確定是否超出等待周期螟蝙;
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// 不需要group后將做釋放操作
dispatch_release(group);
  1. dispatch_group_notify。它以異步的方式工作民傻,當 Dispatch Group中沒有任何任務時胰默,它就會執(zhí)行其代碼场斑,那么 completionBlock便會運行。

代碼示例:

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, ^{
  // 一些異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 
if (completionBlock) { completionBlock(error); }
});

OK!以上即是GCD的一些基本用法牵署。下一部分將是講解GCD的進階編程漏隐,敬請期待。奴迅。青责。

參考文獻:
https://developer.apple.com/library/mac/documentation/Performance/Reference/GCD_libdispatch_Ref/index.html#//apple_ref/c/func/dispatch_set_finalizer_f
https://developer.apple.com/library/mac/documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091
http://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市取具,隨后出現(xiàn)的幾起案子脖隶,更是在濱河造成了極大的恐慌,老刑警劉巖暇检,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件产阱,死亡現(xiàn)場離奇詭異,居然都是意外死亡块仆,警方通過查閱死者的電腦和手機构蹬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悔据,“玉大人庄敛,你說我怎么就攤上這事】坪梗” “怎么了藻烤?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長肛捍。 經(jīng)常有香客問我隐绵,道長,這世上最難降的妖魔是什么拙毫? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任依许,我火速辦了婚禮,結(jié)果婚禮上缀蹄,老公的妹妹穿的比我還像新娘峭跳。我一直安慰自己,他們只是感情好缺前,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布蛀醉。 她就那樣靜靜地躺著,像睡著了一般衅码。 火紅的嫁衣襯著肌膚如雪拯刁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天逝段,我揣著相機與錄音垛玻,去河邊找鬼割捅。 笑死,一個胖子當著我的面吹牛帚桩,可吹牛的內(nèi)容都是我干的亿驾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼账嚎,長吁一口氣:“原來是場噩夢啊……” “哼莫瞬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起郭蕉,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤疼邀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后恳不,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體檩小,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡开呐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年烟勋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筐付。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡卵惦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瓦戚,到底是詐尸還是另有隱情沮尿,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布较解,位于F島的核電站畜疾,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏印衔。R本人自食惡果不足惜啡捶,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奸焙。 院中可真熱鬧瞎暑,春花似錦、人聲如沸与帆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玄糟。三九已至勿她,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阵翎,已是汗流浹背逢并。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工播揪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筒狠。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓猪狈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辩恼。 傳聞我的和親對象是個殘疾皇子雇庙,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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