Swift 3 中的 GCD - 1/2

9/54 Feb20-Feb26 iOS Swift GCD 并發(fā) 譯文

原文

Grand Central Dispatch(GCD)是一種用于管理并發(fā)操作的低級API。 GCD可以提高應(yīng)用程序的響應(yīng)能力冕碟,通過將計算成本高的任務(wù)放到后臺搂根。這是比鎖和線程更容易的并發(fā)模型边锁。

在Swift 3中,GCD進(jìn)行了重大改進(jìn)甜滨,從基于C的API轉(zhuǎn)移到包含新類和新數(shù)據(jù)結(jié)構(gòu)的Swiftier API拇涤。

第一部分將解釋GCD的功能,并展示幾個基本的GCD functions测柠。第二部分中炼鞠,你將了解一些高級功能。

你將構(gòu)建一個GooglyPuff App轰胁。 GooglyPuff是一個非優(yōu)化的“線程不安全”的應(yīng)用程序谒主,使用Core Image的面部檢測API覆蓋檢測到的面部上的眼鏡。您可以從照片庫中選擇要應(yīng)用此效果的圖像赃阀,或選擇從互聯(lián)網(wǎng)下載的圖像霎肯。

你在本教程中的任務(wù)是使用GCD優(yōu)化應(yīng)用程序,并確保你可以安全地從不同的線程調(diào)用代碼榛斯。

開始

下載初始項目观游,Run。

主屏幕初始為空驮俗。點擊+懂缕,然后選擇Le Internet下載預(yù)定義的圖像。點擊第一個圖像王凑,你會看到 googly eyes 添加到臉上提佣。


主要使用四個類:

  • PhotoCollectionViewController:Initial view controller, 顯示縮略圖。
  • PhotoDetailViewController:顯示從PhotoCollectionViewController選擇的照片荤崇,并添加眼球拌屏。
  • Photo:這是描述照片屬性的 Protocol 。它提供了一個圖像术荤,縮略圖及其相應(yīng)的狀態(tài)倚喂。實現(xiàn)協(xié)議的兩個類:DownloadPhoto,其從URL的實例實例化照片,以及 AssetPhoto端圈,實例化來自PHAsset的實例的照片焦读。
  • PhotoManager:這管理所有的Photo對象。

這個App有幾個問題舱权。運行應(yīng)用程序時矗晃,下載完成提醒太早了。你會在系列的第二部分解決這個問題宴倍。

在這個第一部分中张症,你將進(jìn)行一些改進(jìn),包括優(yōu)化 googly-fying 進(jìn)程和使PhotoManager線程安全鸵贬。

GCD概念

要理解GCD俗他,你需要理解并發(fā)和線程相關(guān)的幾個概念勒叠。

并發(fā)

在iOS中佃牛,進(jìn)程或應(yīng)用程序由一個或多個線程組成。線程由操作系統(tǒng)調(diào)度程序獨立管理风响。每個線程可以并發(fā)執(zhí)行嗜浮,但由系統(tǒng)決定是否并發(fā)羡亩、怎樣并發(fā)。

單核設(shè)備可以通過時間分片實現(xiàn)并發(fā)危融。他們將運行一個線程夕春,執(zhí)行上下文切換,然后運行另一個線程专挪。



多核設(shè)備通過并行及志,同時執(zhí)行多個線程。

GCD建立在線程之上寨腔。在底層它管理一個共享線程池速侈。使用GCD,您可以向調(diào)度隊列添加代碼塊或工作項迫卢,由GCD決定執(zhí)行哪些線程倚搬。

在寫代碼時,你發(fā)現(xiàn)一些代碼可以同時運行乾蛤,另外一些不能每界。這樣就允許您使用GCD來并行。

GCD基于系統(tǒng)和可用系統(tǒng)資源決定并行性家卖。需要注意的是眨层,兩個線程并行一定是并發(fā)的,但反之不然上荡。

大致來說趴樱,并發(fā)是關(guān)于結(jié)構(gòu),而并行是關(guān)于執(zhí)行。

隊列

GCD提供由 DispatchQueue 隊列以管理您提交的任務(wù)叁征,并以FIFO順序執(zhí)行它們纳账,以確保提交的第一個任務(wù)是第一個開始的任務(wù)。

DispatchQueue是線程安全的捺疼,這意味著你可以同時從多個線程訪問它們疏虫。當(dāng)你理解調(diào)度隊列如何為代碼的某些部分提供線程安全時,GCD的好處是顯而易見的啤呼。這樣做的關(guān)鍵是選擇正確的DispatchQueue和正確的dispatching功能將您的工作提交到隊列卧秘。

隊列可以是串行或并發(fā)的。串行隊列保證在任何給定時間只運行一個任務(wù)媳友。 GCD控制執(zhí)行時序斯议。你不會知道一個任務(wù)結(jié)束和下一個開始之間的時間产捞。

并發(fā)隊列允許多個任務(wù)同時運行醇锚。保證任務(wù)按照添加的順序啟動。任務(wù)可以以任何順序完成坯临,你不知道下一個任務(wù)要開始的時間焊唬,也不知道在任何時間運行的任務(wù)數(shù)。

參見下面的示例:

注意Task 1看靠,Task 2和Task 3一個接一個地快速啟動赶促。而任務(wù)1 等了一段時間在任務(wù)0之后開始。另外挟炬,雖然任務(wù)3在任務(wù)2之后開始鸥滨,但它更快完成。

何時開始任務(wù)的決定完全由GCD決定谤祖。如果一個任務(wù)的執(zhí)行時間與另一個任務(wù)重疊婿滓,那么由GCD決定是否應(yīng)該在不同的核上運行(如果一個任務(wù)可用),或者改為執(zhí)行上下文切換以運行不同的任務(wù)粥喜。

GCD提供三種主要類型隊列:

  • 主隊列 ( Main Queue) :在主線程上運行凸主,是一個串行隊列。
  • 全局隊列 ( Global Queues) :由整個系統(tǒng)共享的并發(fā)隊列额湘。有四個這樣的隊列具有不同的優(yōu)先級:高卿吐,默認(rèn),低和后臺锋华。后臺是I/O限制嗡官。
  • **自定義隊列 ( Custom queues) **:創(chuàng)建的隊列,可以是串行或并發(fā)的毯焕。這些實際上會被一個全局隊列處理谨湘。

當(dāng)設(shè)置全局并發(fā)隊列時,不直接指定優(yōu)先級。而是指定Quality of Serive(QoS)類屬性紧阔。這將指示任務(wù)的重要性坊罢,并指導(dǎo)GCD確定給予任務(wù)的優(yōu)先級。

QoS類別:

  • 用戶交互式:這表示需要立即完成以提供良好的用戶體驗的任務(wù)擅耽。用于UI更新活孩,事件處理和需要低延遲的小型工作負(fù)載。在執(zhí)行應(yīng)用程序期間乖仇,在此類中完成的工作總量應(yīng)該很小憾儒。這應(yīng)該在主線程上運行。
  • 用戶啟動:表示從UI啟動并可以異步執(zhí)行的任務(wù)乃沙。它應(yīng)該在用戶正在等待即時結(jié)果時使用起趾,并且用于需要繼續(xù)用戶交互的任務(wù)。這將被映射到高優(yōu)先級全局隊列警儒。
  • 實用程序:這表示長時間運行的任務(wù)训裆,通常是進(jìn)度指示條。用于計算蜀铲,I / O边琉,網(wǎng)絡(luò),連續(xù)數(shù)據(jù)和類似任務(wù)记劝。這將被映射到低優(yōu)先級全局隊列变姨。
  • 后臺:這表示用戶不直接感知的任務(wù)。用于提取厌丑,維護(hù)和其他不需要用戶交互并且不對時間敏感的任務(wù)定欧。這將被映射到后臺優(yōu)先級全局隊列。

同步和異步

同GCD怒竿,你可以同步或異步分派任務(wù)砍鸠。

同步函數(shù)在任務(wù)完成后將控制返回給調(diào)用者。

異步函數(shù)立即返回愧口,程序并不等待它執(zhí)行完成睦番。異步函數(shù)不會阻止當(dāng)前執(zhí)行線程,而會繼續(xù)執(zhí)行下一個函數(shù)耍属。

管理任務(wù)

現(xiàn)在你已經(jīng)聽說過任務(wù)了托嚣。為了本教程的目的,您可以將任務(wù)視為閉包 (closure)厚骗。閉包是自包含的示启,可調(diào)用的代碼塊,可以存儲和傳遞领舰。(在Objective-C中叫做 Block)

提交到DispatchQueue的任務(wù)由DispatchWorkItem封裝夫嗓。你可以配置一個DispatchWorkItem的行為迟螺,如它的QoS類或是否產(chǎn)生一個新的分離的線程。

處理后臺任務(wù)

來改進(jìn)App吧舍咖!

添加一些照片從您的照片庫或使用Le Internet選項下載幾張矩父。點擊照片。注意照片詳細(xì)視圖顯示需要多長時間排霉。當(dāng)在較慢的設(shè)備上查看大圖像時窍株,滯后會更明顯。

viewDidLoad() 中太多的工作很容易導(dǎo)致view出現(xiàn)前長時間等待攻柠。如果不是必要的加載球订,可以把它放在后臺執(zhí)行。

這就是一個DispatchQueueAsync 操作瑰钮。

打開PhotoDetailViewController.swift冒滩。修改viewDidLoad() 替換這兩行:

let overlayImage = faceOverlayImageFromImage(image)
fadeInNewImage(overlayImage)

替換代碼:

DispatchQueue.global(qos:.userInitiated).async { // 1 
  let overlayImage = self.faceOverlayImageFromImage(self.image)
  DispatchQueue.main.async { // 2 
    self.fadeInNewImage(overlayImage) // 3
  }
}

解釋:

  1. 將工作移到后臺全局隊列,并以異步方式在閉包中運行工作浪谴。這讓viewDidLoad() 在主線程完成开睡,并使加載感覺更清晰。同時较店,面部檢測處理開始并且將在稍后的時間完成士八。
  2. 面部檢測處理完成容燕,已生成新圖像。因為你想使用這個新的圖像來更新你的UIImageView蘸秘,你設(shè)置image在主隊列官卡。你必須總是在主線程上訪問UIKit類!
  3. 最后醋虏,用fadeInNewImage(_:) 更新UI寻咒,執(zhí)行新的 googly 眼睛圖像的淡入轉(zhuǎn)換。

運行應(yīng)用程序颈嚼。通過Le Internet選項下載照片毛秘。選擇一個照片,你會注意到視圖控制器加載明顯更快阻课,并添加了googly眼睛一個短暫的延遲:

現(xiàn)在叫挟,即使你試圖加載一個巨大的圖像,你的應(yīng)用程序也不會卡頓限煞。

一般來說抹恳。你用async, 當(dāng)你需要執(zhí)行基于網(wǎng)絡(luò)或CPU密集型任務(wù)在后臺署驻,而不是阻塞當(dāng)前線程奋献。

這里是如何使用各種隊列與async

  • 主隊列:這是一個常見的選擇健霹,以在并行隊列中的任務(wù)完成工作后更新UI。調(diào)用async瓶蚂,保證這個新任務(wù)將在當(dāng)前方法結(jié)束后的某個時間在主隊列執(zhí)行糖埋。
  • 全局隊列:這是在后臺執(zhí)行非UI工作的常見選擇。
  • 自定義串行隊列:當(dāng)你想要連續(xù)執(zhí)行后臺工作并跟蹤它窃这。這消除了資源競爭阶捆,因為您知道一次只有一個任務(wù)正在執(zhí)行。請注意钦听,如果您需要來自方法的數(shù)據(jù)洒试,則必須內(nèi)聯(lián)另一個閉包以檢索它或者考慮使用sync。

延遲任務(wù)執(zhí)行

DispatchQueue 允許你延遲任務(wù)執(zhí)行朴上。注意不要使用這個來解決競爭條件或其他時序的bug垒棋,例如引入延遲。當(dāng)您希望任務(wù)在特定時間運行時使用此選項痪宰。

對你的應(yīng)用程序的用戶體驗來說叼架。用戶可能會對他們第一次打開應(yīng)用程序時做什么感到困惑是嗎?

如果沒有任何照片衣撬,最好向用戶顯示提示乖订。你還應(yīng)該考慮用戶如何使用新App。如果你顯示一個提示太快具练,他們可能會錯過它乍构,因為他們的眼睛徘徊在視圖的其他部分。顯示提示之有一秒鐘的延遲足以吸引用戶的注意力并指導(dǎo)他們扛点。

打開PhotoCollectionViewController.swift并在showOrHideNavPrompt() 的實現(xiàn)以下代碼:

let delayInSeconds = 1.0 // 1
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) { // 2
  let count = PhotoManager.sharedManager.photos.count
  if count > 0 {
    self.navigationItem.prompt = nil
  } else {
    self.navigationItem.prompt = "Add photos with faces to Googlyify them!"
  }
}

解釋:

  1. 為延遲時間量指定一個變量哥遮。
  2. 等待指定的時間,然后異步運行更新照片計數(shù)陵究,并更新prompt眠饮。

Build & Run。在顯示提示之前應(yīng)該有一點延遲:

想知道什么時候適合使用asyncAfter铜邮?一般來說仪召,在主隊列中使用它是一個不錯的選擇。在其他隊列(如全局后臺隊列或自定義串行隊列)上使用asyncAfter時松蒜,您需要小心扔茅。

為什么不使用Timer?你可以考慮使用它牍鞠,如果你有重復(fù)的任務(wù)咖摹。這里有兩個原因用asyncAfter

  1. 一個是可讀性难述。要使用Timer萤晴,您必須定義一個方法吐句,然后使用選擇器或調(diào)用定義的方法創(chuàng)建計時器。使用DispatchQueue和async只需添加一個閉包店读。
  2. Timer 被安排在Run Loop上嗦枢,所以你還必須確保它被安排在正確的Run Loop上。在這方面屯断,使用調(diào)度隊列更容易文虏。

管理 Singletons

Singletons,在iOS中非常流行殖演。

Singletons 的一個常見問題是氧秘,它們通常不是線程安全的。因為它們通常被多個Controller 同時訪問趴久。PhotoManager是一個單例丸相,所以你需要考慮這個問題。

線程安全的代碼可以安全地從多個線程或并發(fā)任務(wù)調(diào)用彼棍,而不會導(dǎo)致任何問題灭忠。非線程安全的代碼只能一次在一個上下文中運行。

在單例實例的初始化期間以及在對實例的讀取和寫入期間座硕,需要考慮兩種線程安全性情況弛作。

初始化是容易的情況,這是由于Swift初始化全局變量的方式华匾。全局變量在首次訪問時被初始化映琳,并且它們被保證以原子方式初始化。也就是說瘦真,執(zhí)行初始化的代碼被視為原子操作刊头,保證在任何其他線程訪問全局變量之前完成黍瞧。

打開PhotoManager.swift查看如何初始化單例:

private let _sharedManager = PhotoManager()

私有全局 _sharedManager 變量用于惰性初始化 PhotoManager诸尽。 這僅發(fā)生在第一次訪問:_

class var sharedManager: PhotoManager {
  return _sharedManager
}

公共函數(shù)sharedManager 返回私有_sharedManager變量。 Swift確保此操作是線程安全的_

在訪問操作共享內(nèi)部數(shù)據(jù)的單例中的代碼時印颤,仍然需要處理線程安全您机。可以通過同步數(shù)據(jù)訪問等方法來處理此問題年局。在下一節(jié)中际看,您將看到一種方法。

處理 Readers-Writers 問題

在Swift中矢否,用let關(guān)鍵字聲明的任何變量都被認(rèn)為是一個常量仲闽,并且是只讀的和線程安全的。然而僵朗,使用var關(guān)鍵字聲明變量赖欣,它是可變的屑彻,并且不是線程安全的,除非數(shù)據(jù)類型被設(shè)計為這樣顶吮。 Swift集合類型(如ArrayDictionary)在聲明為可變時不是線程安全的社牲。

雖然許多線程可以同時讀取Array的可變實例,這沒有問題悴了,但是讓一個線程讀搏恤,讓一個線程修改數(shù)組是不安全的。你的單例不能防止這種情況發(fā)生湃交。

讓我們看下 addPhoto(_:)PhotoManager.swift

func addPhoto(_ photo: Photo) {
  _photos.append(photo)
  DispatchQueue.main.async {
    self.postContentAddedNotification()
  }
}

這是一個寫方法熟空,因為它修改了一個可變數(shù)組對象。

現(xiàn)在來看看photos

fileprivate var _photos: [Photo] = [ ]
var photos: [Photo] {
  return _photos
}

此屬性的getter被稱為讀取方法搞莺,因為它正在讀取mutable數(shù)組痛阻。 調(diào)用者獲得數(shù)組的副本,并且防止不適當(dāng)?shù)馗淖冊紨?shù)組腮敌。 這不提供任何保護(hù)阱当,以防止一個線程調(diào)用addPhoto(_:),而另一個線程調(diào)用getter的photos屬性糜工。_

注意:在上面的代碼中弊添,為什么調(diào)用者得到的照片數(shù)組的副本? 在Swift中捌木,參數(shù)和返回類型的函數(shù)通過引用或值傳遞油坝。
按值傳遞將導(dǎo)致對象的副本,對副本的更改不會影響原始副本刨裆。 默認(rèn)情況下澈圈,在Swift類中,實例通過引用傳遞帆啃,而struct通過值傳遞瞬女。 Swift的內(nèi)置數(shù)據(jù)類型,如數(shù)組和字典努潘,被實現(xiàn)為結(jié)構(gòu)體诽偷。
看起來當(dāng)你來回傳遞集合時,代碼中有很多復(fù)制疯坤。 不要擔(dān)心這種內(nèi)存使用的影響报慕。 Swift集合類型被優(yōu)化以僅在必要時進(jìn)行復(fù)制,例如當(dāng)通過值的數(shù)組在傳遞之后第一次被修改時压怠。

這是經(jīng)典的Readers-Writers問題眠冈。 GCD提供了一個優(yōu)雅解決方案創(chuàng)建讀寫鎖,通過使用 dispatch barriers菌瘫。dispatch barriers是在使用并發(fā)隊列時作為串行式瓶頸的一組函數(shù)蜗顽。

當(dāng)您向調(diào)度隊列提交DispatchWorkItem時玄柠,您可以設(shè)置標(biāo)志以指示它應(yīng)該是在特定時間在該指定隊列上執(zhí)行的唯一項目。 這意味著提交到隊列之前的所有項目必須在DispatchWorkItem執(zhí)行之前完成诫舅。

當(dāng)輪到DispatchWorkItem時羽利,GCD確保隊列在那段時間內(nèi)不執(zhí)行任何其他任務(wù)。 一旦完成刊懈,隊列返回到其默認(rèn)狀態(tài)这弧。

下圖說明了barriers對各種異步任務(wù)的影響:

注意在正常操作中隊列的行為就像一個普通的并發(fā)隊列。 但是當(dāng)barriers執(zhí)行時虚汛,它本質(zhì)上像一個串行隊列匾浪。 也就是說,barriers是唯一執(zhí)行的任務(wù)卷哩。 在barriers完成后蛋辈,隊列返回到正常的并發(fā)隊列。

在全局后臺并行隊列中使用barriers時請謹(jǐn)慎将谊,因為這些隊列是共享資源冷溶。 在自定義串行隊列中使用障礙是多余的,因為它已經(jīng)連續(xù)執(zhí)行尊浓。 在自定義并發(fā)隊列中使用barriers是最好的選擇逞频。

你將使用自定義并發(fā)隊列來處理您的barrier功能并隔離讀取和寫入功能。 并發(fā)隊列將允許同時進(jìn)行多個讀取操作栋齿。

打開PhotoManager.swift并在_photos聲明之上添加一個私有屬性:

fileprivate let concurrentPhotoQueue =
  DispatchQueue(
    label: "com.raywenderlich.GooglyPuff.photoQueue", // 1
    attributes: .concurrent) // 2

這會將concurrentPhotoQueue初始化為并發(fā)隊列苗胀。

  1. 您在調(diào)試期間使用描述性名稱設(shè)置標(biāo)簽,這是有幫助的瓦堵。 通常基协,您將使用顛倒的DNS樣式命名約定。
  2. 指定并發(fā)隊列菇用。

接下來澜驮,使用以下代碼替換addPhoto(_:)

func addPhoto(_ photo: Photo) {
  concurrentPhotoQueue.async(flags: .barrier) { // 1
    self._photos.append(photo) // 2
    DispatchQueue.main.async { // 3
      self.postContentAddedNotification()
    }
  }
}

這里是工作原理:

  1. barrier異步分派寫操作。當(dāng)它執(zhí)行時刨疼,它將是隊列中的唯一項目泉唁。
  2. 將對象添加到數(shù)組。
  3. 最后揩慕,您發(fā)布了您添加了照片的通知。這個通知應(yīng)該發(fā)布在主線程上扮休,因為它會做UI工作迎卤。因此,您將另一個任務(wù)異步分派到主隊列以觸發(fā)通知玷坠。

還需要實現(xiàn)照片讀取方法蜗搔。

為了確保線程安全劲藐,您需要在concurrentPhotoQueue隊列上執(zhí)行讀取。您需要從函數(shù)調(diào)用返回數(shù)據(jù)樟凄,因此異步調(diào)度不會打斷它聘芜。在這種情況下,syc將是一個很好的候選缝龄。

使用sync來跟蹤您的工作與調(diào)度障礙汰现,或當(dāng)您需要等待操作完成,然后才能使用由閉包處理的數(shù)據(jù)叔壤。

你需要小心瞎饲。如果你調(diào)用sync并且當(dāng)前隊列已經(jīng)運行。這將導(dǎo)致死鎖情況炼绘。

兩個(或有時更多)項目 - 在大多數(shù)情況下嗅战,線程 - 被稱為死鎖,如果他們都卡住等待對方完成或執(zhí)行另一個操作俺亮。第一個不能完成驮捍,因為它在等待第二個完成。但第二個不能完成脚曾,因為它在等待第一個完成厌漂。

以下是使用同步功能的時間和位置:

  • 主隊列:非常小心,這種情況也有潛在的死鎖狀態(tài)斟珊。
  • 全局隊列:這是一個很好的候選苇倡,通過調(diào)度障礙或等待任務(wù)完成同步工作,所以你可以執(zhí)行進(jìn)一步處理囤踩。
  • 自定義序列隊列:非常小心旨椒,如果您在隊列中運行并調(diào)用sync 鎖定同一個隊列,那么肯定會導(dǎo)致死鎖堵漱。

仍然在PhotoManager.swift中修改photos屬性getter:

var photos: [Photo] {
  var photosCopy: [Photo]!
  concurrentPhotoQueue.sync { // 1
    photosCopy = self._photos // 2
  }
  return photosCopy
}

以下是逐步進(jìn)行的操作:

  1. sync分派到concurrentPhotoQueue上以執(zhí)行讀取综慎。
  2. 將照片陣列的副本存儲在photosCopy中并返回。

構(gòu)建并運行應(yīng)用程序勤庐。 通過Le Internet選項下載照片示惊。 它應(yīng)該像以前一樣,但在底下愉镰,你有一些健康的線程米罚。

恭喜 - PhotoManager Singleton 現(xiàn)在是線程安全的。無論在哪里或如何讀或?qū)懭胝掌商剑愣伎梢韵嘈潘鼤园踩姆绞酵瓿伞?/p>

下一步

在這個Grand Central Dispatch教程中录择,您學(xué)習(xí)了如何使代碼線程安全审丘,以及如何在執(zhí)行CPU密集型任務(wù)時保持主線程的響應(yīng)员帮。

您可以下載已完成的項目,其中包含所有改進(jìn)。在本教程的第二部分中昌妹,你將繼續(xù)改進(jìn)此項目议忽。

如果您打算優(yōu)化自己的應(yīng)用程序恃鞋,您應(yīng)該使用Instruments中的Time Profile文件模板來分析您的工作洞慎。

你也許還想看看Rob Pike對并發(fā)與并行性的這個精彩演講

我們的iOS并發(fā)與GCD和操作視頻教程系列也涵蓋了很多我們在本教程中涵蓋的相同的主題菱皆。

在本教程的下一部分须误,您將深入了解GCD的API,以做更酷的東西搔预。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末霹期,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拯田,更是在濱河造成了極大的恐慌历造,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件船庇,死亡現(xiàn)場離奇詭異吭产,居然都是意外死亡,警方通過查閱死者的電腦和手機鸭轮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門臣淤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窃爷,你說我怎么就攤上這事邑蒋。” “怎么了按厘?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵医吊,是天一觀的道長。 經(jīng)常有香客問我逮京,道長卿堂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任懒棉,我火速辦了婚禮草描,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘策严。我一直安慰自己穗慕,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布享钞。 她就那樣靜靜地躺著揍诽,像睡著了一般诀蓉。 火紅的嫁衣襯著肌膚如雪栗竖。 梳的紋絲不亂的頭發(fā)上暑脆,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音狐肢,去河邊找鬼添吗。 笑死,一個胖子當(dāng)著我的面吹牛份名,可吹牛的內(nèi)容都是我干的碟联。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼僵腺,長吁一口氣:“原來是場噩夢啊……” “哼鲤孵!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辰如,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤普监,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后琉兜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凯正,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年豌蟋,在試婚紗的時候發(fā)現(xiàn)自己被綠了廊散。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡梧疲,死狀恐怖允睹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情幌氮,我是刑警寧澤缭受,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站浩销,受9級特大地震影響贯涎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜慢洋,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一塘雳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧普筹,春花似錦败明、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酸员。三九已至,卻和暖如春讳嘱,著一層夾襖步出監(jiān)牢的瞬間幔嗦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工沥潭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留邀泉,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓钝鸽,卻偏偏與公主長得像汇恤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拔恰,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

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