iOS多線程-各種線程鎖的簡單介紹

demo下載

建議一邊看文章,一邊看代碼来涨。

聲明:關(guān)于性能的分析是基于我的測試代碼來的图焰,我也看到和網(wǎng)上很多測試結(jié)果有所不同,所以性能分析只作為參考蹦掐,僅代表測試代碼表現(xiàn)技羔,不代表真實情況。同時我會基于我的代碼盡量讓性能測試更精準卧抗。

線程安全是怎么產(chǎn)生的

常見比如線程內(nèi)操作了一個線程外的非線程安全變量藤滥,這個時候一定要考慮線程安全和同步。

- (void)getIamgeName:(NSMutableArray *)imageNames{//假如每個進來的都是一個線程

/*1.imageNames是線程外的變量社裆,這個時候就需要考慮線程安全拙绊,

因為,假如我們當前imageNames的個數(shù)是1泳秀,線程A和B同時進來發(fā)現(xiàn)個數(shù)是大于0的标沪,

都會去執(zhí)行remove操作,結(jié)果肯定會有一個線程崩潰掉嗜傅。

*/

/*2.NSMutableArray *array = [[NSMutableArray alloc]initWithArray:imageNames];

這里如果新生成一個array谨娜,下面也把imageNames換成array就不需要考慮線程安全,

但是這樣array.count判斷永遠大于0磺陡,也就是永遠等于imageNames.count

*/

NSString *imageName;

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObject:imageName];

}

}

下面是鎖的同步方案

鎖的概念

鎖是最常用的同步工具。一段代碼段在同一個時間只能允許被一個線程訪問,比如一個線程A進入加鎖代碼之后由于已經(jīng)加鎖币他,另一個線程B就無法訪問坞靶,只有等待前一個線程A執(zhí)行完加鎖代碼后解鎖,B線程才能訪問加鎖代碼蝴悉。

不要將過多的其他操作代碼放到里面彰阴,否則一個線程執(zhí)行的時候另一個線程就一直在等待,就無法發(fā)揮多線程的作用了拍冠。

NSLock

在Cocoa程序中NSLock中實現(xiàn)了一個簡單的互斥鎖尿这,實現(xiàn)了NSLocking protocol。

lock庆杜,加鎖

unlock射众,解鎖

tryLock,嘗試加鎖晃财,如果失敗了叨橱,并不會阻塞線程,只是立即返回

NOlockBeforeDate:断盛,在指定的date之前暫時阻塞線程(如果沒有獲取鎖的話)罗洗,如果到期還沒有獲取鎖,則線程被喚醒钢猛,函數(shù)立即返回NO

使用tryLock并不能成功加鎖伙菜,如果獲取鎖失敗就不會執(zhí)行加鎖代碼了。

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

[lock lock];

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObject:imageName];

}

[lock unlock];

}

@synchronized代碼塊

每個iOS開發(fā)最早接觸的線程鎖就是@synchronized命迈,代碼簡單贩绕。

- (void)getIamgeName:(int)index{

NSString *imageName;

@synchronized(self) {

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObject:imageName];

}

}

}

條件信號量dispatch_semaphore_t

dispatch_semaphore_tGCD中信號量,也可以解決資源搶占問題,支持信號通知和信號等待躺翻。每當發(fā)送一個信號通知丧叽,則信號量+1;每當發(fā)送一個等待信號時信號量-1,公你;如果信號量為0則信號會處于等待狀態(tài)踊淳,直到信號量大于0開始執(zhí)行。

#import "MYDispatchSemaphoreViewController.h"

@interface MYDispatchSemaphoreViewController ()

{

dispatch_semaphore_t semaphore;

}

@end

@implementation MYDispatchSemaphoreViewController

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view.

semaphore = dispatch_semaphore_create(1);

/**

*? 創(chuàng)建一個信號量為1的信號

*

*/

}

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

/**

*? semaphore:等待信號

DISPATCH_TIME_FOREVER:等待時間

wait之后信號量-1陕靠,為0

*/

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObject:imageName];

}

/**

*? 發(fā)送一個信號通知迂尝,這時候信號量+1,為1

*/

dispatch_semaphore_signal(semaphore);

}

@end

條件鎖NSCondition

NSCondition同樣實現(xiàn)了NSLocking協(xié)議剪芥,所以它和NSLock一樣垄开,也有NSLocking協(xié)議的lock和unlock方法,可以當做NSLock來使用解決線程同步問題税肪,用法完全一樣溉躲。

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

[lock lock];

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObject:imageName];

}

[lock unlock];

}

同時榜田,NSCondition提供更高級的用法。wait和signal锻梳,和條件信號量類似箭券。

比如我們要監(jiān)聽imageNames數(shù)組的個數(shù),當imageNames的個數(shù)大于0的時候就執(zhí)行清空操作疑枯。思路是這樣的辩块,當imageNames個數(shù)大于0時執(zhí)行清空操作,否則荆永,wait等待執(zhí)行清空操作废亭。當imageNames個數(shù)增加的時候發(fā)生signal信號,讓等待的線程喚醒繼續(xù)執(zhí)行具钥。

NSCondition和NSLock豆村、@synchronized等是不同的是,NSCondition可以給每個線程分別加鎖氓拼,加鎖后不影響其他線程進入臨界區(qū)你画。這是非常強大。

但是正是因為這種分別加鎖的方式桃漾,NSCondition使用wait并使用加鎖后并不能真正的解決資源的競爭坏匪。比如我們有個需求:不能讓m<0。假設(shè)當前m=0,線程A要判斷到m>0為假,執(zhí)行等待撬统;線程B執(zhí)行了m=1操作适滓,并喚醒線程A執(zhí)行m-1操作的同時線程C判斷到m>0,因為他們在不同的線程鎖里面恋追,同樣判斷為真也執(zhí)行了m-1凭迹,這個時候線程A和線程C都會執(zhí)行m-1,但是m=1,結(jié)果就會造成m=-1.

當我用數(shù)組做刪除試驗時苦囱,做增刪操作并不是每次都會出現(xiàn)嗅绸,大概3-4次后會出現(xiàn)。單純的使用lock撕彤、unlock是沒有問題的鱼鸠。

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

[lock lock];? ? //加鎖

static int m = 0;

static int n = 0;

static int p = 0;

NSLog(@"removeObjectBegin count: %ld\n",imageNames.count);

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObjectAtIndex:0];

m++;

NSLog(@"執(zhí)行了%d次刪除操作",m);

} else {

p++;

NSLog(@"執(zhí)行了%d次等待",p);

[lock wait];? ? //等待

imageName = [imageNames lastObject];

[imageNames removeObjectAtIndex:0];

/**

*? 有時候點擊取出圖片會崩潰

*/

n++;

NSLog(@"執(zhí)行了%d次繼續(xù)操作",n);

}

NSLog(@"removeObject count: %ld\n",imageNames.count);

[lock unlock];? ? //解鎖

}

- (void)createImageName:(NSMutableArray *)imageNames{

[lock lock];

static int m = 0;

[imageNames addObject:@"0"];

m++;

NSLog(@"添加了%d次",m);

[lock signal];? //喚醒隨機一個線程取消等待繼續(xù)執(zhí)行

//? ? ? ? [lock broadcast];? //喚醒所有線程取消等待繼續(xù)執(zhí)行

NSLog(@"createImageName count: %ld\n",imageNames.count);

[lock unlock];

}

#pragma mark - 多線程取出圖片后刪除

- (void)getImageNameWithMultiThread{

[lock broadcast];

NSMutableArray *imageNames = [[NSMutableArray alloc]init];

dispatch_group_t dispatchGroup = dispatch_group_create();

__block double then, now;

then = CFAbsoluteTimeGetCurrent();

for (int i=0; i<10; i++) {

dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){

[self getIamgeName:imageNames];

});

dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){

[self createImageName:imageNames];

});

}

dispatch_group_notify(dispatchGroup, self.synchronizationQueue, ^(){

now = CFAbsoluteTimeGetCurrent();

printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count);

});

}

條件鎖NSConditionLock

也有人說這是個互斥鎖

NSConditionLock同樣實現(xiàn)了NSLocking協(xié)議,試驗過程中發(fā)現(xiàn)性能很低羹铅。

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

[lock lock];

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObject:imageName];

}

[lock unlock];

}

NSConditionLock也可以像NSCondition一樣做多線程之間的任務(wù)等待調(diào)用蚀狰,而且是線程安全的。

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

[lock lockWhenCondition:1];? ? //加鎖

if (imageNames.count>0) {

imageName = [imageNames lastObject];

[imageNames removeObjectAtIndex:0];

}

[lock unlockWithCondition:0];? ? //解鎖

}

- (void)createImageName:(NSMutableArray *)imageNames{

[lock lockWhenCondition:0];

[imageNames addObject:@"0"];

[lock unlockWithCondition:1];

}

#pragma mark - 多線程取出圖片后刪除

- (void)getImageNameWithMultiThread{

NSMutableArray *imageNames = [[NSMutableArray alloc]init];

dispatch_group_t dispatchGroup = dispatch_group_create();

__block double then, now;

then = CFAbsoluteTimeGetCurrent();

for (int i=0; i<10000; i++) {

dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){

[self getIamgeName:imageNames];

});

dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){

[self createImageName:imageNames];

});

}

dispatch_group_notify(dispatchGroup, self.synchronizationQueue, ^(){

now = CFAbsoluteTimeGetCurrent();

printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count);

});

}

遞歸鎖NSRecursiveLock

有時候“加鎖代碼”中存在遞歸調(diào)用职员,遞歸開始前加鎖麻蹋,遞歸調(diào)用開始后會重復執(zhí)行此方法以至于反復執(zhí)行加鎖代碼最終造成死鎖,這個時候可以使用遞歸鎖來解決焊切。使用遞歸鎖可以在一個線程中反復獲取鎖而不造成死鎖扮授,這個過程中會記錄獲取鎖和釋放鎖的次數(shù)芳室,只有最后兩者平衡鎖才被最終釋放。

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

[lock lock];

if (imageNames.count>0) {

imageName = [imageNames firstObject];

[imageNames removeObjectAtIndex:0];

[self getIamgeName:imageNames];

}

[lock unlock];

}

- (void)getImageNameWithMultiThread{

NSMutableArray *imageNames = [NSMutableArray new];

int count = 1024*10;

for (int i=0; i

[imageNames addObject:[NSString stringWithFormat:@"%d",i]];

}

dispatch_group_t dispatchGroup = dispatch_group_create();

__block double then, now;

then = CFAbsoluteTimeGetCurrent();

dispatch_group_async(dispatchGroup, self.synchronizationQueue, ^(){

[self getIamgeName:imageNames];

});

dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){

now = CFAbsoluteTimeGetCurrent();

printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count);

});

}

NSDistributedLock

NSDistributedLock是MAC開發(fā)中的跨進程的分布式鎖刹勃,底層是用文件系統(tǒng)實現(xiàn)的互斥鎖渤愁。NSDistributedLock沒有實現(xiàn)NSLocking協(xié)議,所以沒有l(wèi)ock方法深夯,取而代之的是非阻塞的tryLock方法。

NSDistributedLock *lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/lock.lock"];

while (![lock tryLock])

{

sleep(1);

}

//do something

[lock unlock];

當執(zhí)行到do something時程序退出,程序再次啟動之后tryLock就再也不能成功了,陷入死鎖狀態(tài).其他應(yīng)用也不能訪問受保護的共享資源诺苹。在這種情況下咕晋,你可以使用breadLock方法來打破現(xiàn)存的鎖以便你可以獲取它。但是通常應(yīng)該避免打破鎖收奔,除非你確定擁有進程已經(jīng)死亡并不可能再釋放該鎖掌呜。

因為是MAC下的線程鎖,所以demo里面沒有坪哄,這里也不做過多關(guān)注质蕉。

互斥鎖POSIX

POSIX和dispatch_semaphore_t很像,但是完全不同翩肌。POSIX是Unix/Linux平臺上提供的一套條件互斥鎖的API模暗。

新建一個簡單的POSIX互斥鎖,引入頭文件#import 聲明并初始化一個pthread_mutex_t的結(jié)構(gòu)念祭。使用pthread_mutex_lock和pthread_mutex_unlock函數(shù)兑宇。調(diào)用pthread_mutex_destroy來釋放該鎖的數(shù)據(jù)結(jié)構(gòu)。

#import

@interface MYPOSIXViewController ()

{

pthread_mutex_t mutex;? //聲明pthread_mutex_t的結(jié)構(gòu)

}

@end

@implementation MYPOSIXViewController

- (void)dealloc{

pthread_mutex_destroy(&mutex);? //釋放該鎖的數(shù)據(jù)結(jié)構(gòu)

}

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view.

pthread_mutex_init(&mutex, NULL);

/**

*? 初始化

*

*/

}

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

/**

*? 加鎖

*/

pthread_mutex_lock(&mutex);

if (imageNames.count>0) {

imageName = [imageNames firstObject];

[imageNames removeObjectAtIndex:0];

}

/**

*? 解鎖

*/

pthread_mutex_unlock(&mutex);

}

POSIX還可以創(chuàng)建條件鎖粱坤,提供了和NSCondition一樣的條件控制隶糕,初始化互斥鎖同時使用pthread_cond_init來初始化條件數(shù)據(jù)結(jié)構(gòu),

// 初始化

int pthread_cond_init (pthread_cond_t *cond, pthread_condattr_t *attr);

// 等待(會阻塞)

int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut);

// 定時等待

int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mut, const struct timespec *abstime);

// 喚醒

int pthread_cond_signal (pthread_cond_t *cond);

// 廣播喚醒

int pthread_cond_broadcast (pthread_cond_t *cond);

// 銷毀

int pthread_cond_destroy (pthread_cond_t *cond);

POSIX還提供了很多函數(shù)站玄,有一套完整的API枚驻,包含Pthreads線程的創(chuàng)建控制等等,非常底層株旷,可以手動處理線程的各個狀態(tài)的轉(zhuǎn)換即管理生命周期再登,甚至可以實現(xiàn)一套自己的多線程,感興趣的可以繼續(xù)深入了解灾常。推薦一篇詳細文章霎冯,但不是基于iOS的,是基于Linux的钞瀑,但是介紹的非常詳細Linux 線程鎖詳解

自旋鎖OSSpinLock

首先要提的是OSSpinLock已經(jīng)出現(xiàn)了BUG沈撞,導致并不能完全保證是線程安全的。

新版 iOS 中雕什,系統(tǒng)維護了 5 個不同的線程優(yōu)先級/QoS: background缠俺,utility显晶,default,user-initiated壹士,user-interactive磷雇。高優(yōu)先級線程始終會在低優(yōu)先級線程前執(zhí)行,一個線程不會受到比它更低優(yōu)先級線程的干擾躏救。這種線程調(diào)度算法會產(chǎn)生潛在的優(yōu)先級反轉(zhuǎn)問題唯笙,從而破壞了 spin lock。

具體來說盒使,如果一個低優(yōu)先級的線程獲得鎖并訪問共享資源崩掘,這時一個高優(yōu)先級的線程也嘗試獲得這個鎖,它會處于 spin lock 的忙等狀態(tài)從而占用大量 CPU少办。此時低優(yōu)先級線程無法與高優(yōu)先級線程爭奪 CPU 時間苞慢,從而導致任務(wù)遲遲完不成、無法釋放 lock英妓。這并不只是理論上的問題挽放,libobjc 已經(jīng)遇到了很多次這個問題了,于是蘋果的工程師停用了 OSSpinLock蔓纠。

蘋果工程師 Greg Parker 提到辑畦,對于這個問題,一種解決方案是用 truly unbounded backoff 算法贺纲,這能避免 livelock 問題航闺,但如果系統(tǒng)負載高時,它仍有可能將高優(yōu)先級的線程阻塞數(shù)十秒之久猴誊;另一種方案是使用 handoff lock 算法潦刃,這也是 libobjc 目前正在使用的。鎖的持有者會把線程 ID 保存到鎖內(nèi)部懈叹,鎖的等待者會臨時貢獻出它的優(yōu)先級來避免優(yōu)先級反轉(zhuǎn)的問題乖杠。理論上這種模式會在比較復雜的多鎖條件下產(chǎn)生問題,但實踐上目前還一切都好澄成。

OSSpinLock 自旋鎖胧洒,性能最高的鎖。原理很簡單墨状,就是一直 do while 忙等卫漫。它的缺點是當?shù)却龝r會消耗大量 CPU 資源,所以它不適用于較長時間的任務(wù)肾砂。對于內(nèi)存緩存的存取來說列赎,它非常合適。

-摘自ibireme

所以說不建議再繼續(xù)使用镐确,不過可以拿來玩耍一下,導入頭文件#import

#import

@interface MYOSSpinLockViewController ()

{

OSSpinLock spinlock;? //聲明pthread_mutex_t的結(jié)構(gòu)

}

@end

@implementation MYOSSpinLockViewController

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view.

spinlock = OS_SPINLOCK_INIT;

/**

*? 初始化

*

*/

}

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

/**

*? 加鎖

*/

OSSpinLockLock(&spinlock);

if (imageNames.count>0) {

imageName = [imageNames firstObject];

[imageNames removeObjectAtIndex:0];

}

/**

*? 解鎖

*/

OSSpinLockUnlock(&spinlock);

}

@end

OSSpinLock的性能真的很卓越包吝,可惜啦

GCD線程阻斷dispatch_barrier_async/dispatch_barrier_sync

dispatch_barrier_async/dispatch_barrier_sync在一定的基礎(chǔ)上也可以做線程同步饼煞,會在線程隊列中打斷其他線程執(zhí)行當前任務(wù),也就是說只有用在并發(fā)的線程隊列中才會有效诗越,因為串行隊列本來就是一個一個的執(zhí)行的砖瞧,你打斷執(zhí)行一個和插入一個是一樣的效果。兩個的區(qū)別是是否等待任務(wù)執(zhí)行完成嚷狞。

注意:如果在當前線程調(diào)用dispatch_barrier_sync打斷會發(fā)生死鎖块促。

@interface MYdispatch_barrier_syncViewController ()

{

__block double then, now;

}

@end

@implementation MYdispatch_barrier_syncViewController

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view.

}

- (void)getIamgeName:(NSMutableArray *)imageNames{

NSString *imageName;

if (imageNames.count>0) {

imageName = [imageNames firstObject];

[imageNames removeObjectAtIndex:0];

}else{

now = CFAbsoluteTimeGetCurrent();

printf("thread_lock: %f sec\nimageNames count: %ld\n", now-then,imageNames.count);

}

}

- (void)getImageNameWithMultiThread{

NSMutableArray *imageNames = [NSMutableArray new];

int count = 1024*11;

for (int i=0; i

[imageNames addObject:[NSString stringWithFormat:@"%d",i]];

}

then = CFAbsoluteTimeGetCurrent();

for (int i=0; i

//100來測試鎖有沒有正確的執(zhí)行

dispatch_barrier_async(self.synchronizationQueue, ^{

[self getIamgeName:imageNames];

});

}

}

總結(jié)

@synchronized:適用線程不多,任務(wù)量不大的多線程加鎖

NSLock:其實NSLock并沒有想象中的那么差床未,不知道大家為什么不推薦使用

dispatch_semaphore_t:使用信號來做加鎖褂乍,性能提升顯著

NSCondition:使用其做多線程之間的通信調(diào)用不是線程安全的

NSConditionLock:單純加鎖性能非常低,比NSLock低很多即硼,但是可以用來做多線程處理不同任務(wù)的通信調(diào)用

NSRecursiveLock:遞歸鎖的性能出奇的高,但是只能作為遞歸使用,所以限制了使用場景

NSDistributedLock:因為是MAC開發(fā)的屡拨,就不討論了

POSIX(pthread_mutex):底層的api只酥,復雜的多線程處理建議使用,并且可以封裝自己的多線程

OSSpinLock:性能也非常高呀狼,可惜出現(xiàn)了線程問題

dispatch_barrier_async/dispatch_barrier_sync:測試中發(fā)現(xiàn)dispatch_barrier_sync比dispatch_barrier_async性能要高裂允,真是大出意外

下面是基準測試

模擬器環(huán)境:i5 2.6GH+8G 內(nèi)存,xcode? 7.2.1 (7C1002)+iPhone6SP(9.2)

多線程鎖刪除數(shù)組性能測試(模擬器).png

真機環(huán)境:xcode? 7.2.1 (7C1002)+iPhone6(國行)

多線程鎖刪除數(shù)組性能測試(iPhone6真機).png

通過測試發(fā)現(xiàn)模擬器和真機的區(qū)別還是很大的哥艇,模擬器上明顯的階梯感绝编,真機就沒有,模擬器上NSConditionLock的性能非常差貌踏,我沒有把它的參數(shù)加在表格上十饥,不然其他的就看不到了。不過真機上面性能還好祖乳。

這些性能測試只是一個參考逗堵,沒必要非要去在意這些,畢竟前端的編程一般線程要求沒那么高眷昆,可以從其他的地方優(yōu)化蜒秤。線程安全中注意避坑,另外選擇自己喜歡的方式亚斋,這樣你可以研究的更深入作媚,使用的更熟練。

另外帅刊,demo中我把邏輯拿了出來纸泡,算是一個小小的MVVM框架或者MVVCC框架吧

demo在最上方。

2016.6.30更新

有網(wǎng)友提醒我有些鎖在資源競爭激烈和不激烈的情況下性能有差別厚掷,于是我修改了源碼弟灼,將原來的開辟大量線程邏輯改為開辟3個線程级解,代碼已更新github,老代碼在標簽1.0的位置田绑,有興趣可以看下勤哗。

多線程鎖刪除數(shù)組性能測試(iPhone6真機)2.png

根據(jù)新的測試結(jié)果,dispatch_barrier_async掩驱、dispatch_semaphore_t? ? 芒划、OSSpinLock,三種鎖在資源競爭程度不同下表現(xiàn)比較明顯欧穴。

另外再次聲明:測試結(jié)果僅僅代表一個參考民逼,因為各種因素的影響,并沒有那么準確涮帘。還是那句話拼苍,選擇自己喜歡的加鎖方式,高大上的還是性能高的调缨,自己選擇疮鲫,沒必要太在意對比。

作者:Yasin的簡書

鏈接:http://www.reibang.com/p/35dd92bcfe8c

來源:簡書

著作權(quán)歸作者所有弦叶。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)俊犯,非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伤哺,一起剝皮案震驚了整個濱河市燕侠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌立莉,老刑警劉巖绢彤,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜓耻,居然都是意外死亡杖虾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人号胚,你說我怎么就攤上這事叶洞。” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我皮仁,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任贷祈,我火速辦了婚禮趋急,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘势誊。我一直安慰自己呜达,他們只是感情好,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布粟耻。 她就那樣靜靜地躺著查近,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挤忙。 梳的紋絲不亂的頭發(fā)上霜威,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機與錄音册烈,去河邊找鬼戈泼。 笑死,一個胖子當著我的面吹牛赏僧,可吹牛的內(nèi)容都是我干的矮冬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼次哈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吆录?” 一聲冷哼從身側(cè)響起窑滞,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恢筝,沒想到半個月后哀卫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡撬槽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年此改,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侄柔。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡共啃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出暂题,到底是詐尸還是另有隱情移剪,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布薪者,位于F島的核電站纵苛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜攻人,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一取试、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怀吻,春花似錦瞬浓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渣窜,卻和暖如春铺根,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乔宿。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工位迂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人详瑞。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓掂林,卻偏偏與公主長得像,于是被迫代替她去往敵國和親坝橡。 傳聞我的和親對象是個殘疾皇子泻帮,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

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