IOS---多線程實(shí)現(xiàn)方案一 (pthread柳琢、NSThread)

IOS多線程實(shí)現(xiàn)方案一 (pthread、NSThread)

在iOS開發(fā)中润脸,多線程是我們?cè)陂_發(fā)中經(jīng)常使用的一門技術(shù)柬脸。那么本文章將和大家探討一下針對(duì)于多線程的技術(shù)實(shí)現(xiàn)。本文主要分為如下幾個(gè)部分:

  • iOS開發(fā)中實(shí)現(xiàn)多線程的方式
  • 單線程
  • pthread
  • NSThread

一毙驯、iOS開發(fā)中實(shí)現(xiàn)多線程的方式

  1. pthread: 跨平臺(tái)倒堕,適用于多種操作系統(tǒng),可移植性強(qiáng)爆价,是一套純C語(yǔ)言的通用API垦巴,且線程的生命周期需要程序員自己管理媳搪,使用難度較大,所以在實(shí)際開發(fā)中通常不使用骤宣。
  2. NSThread: 基于OC語(yǔ)言的API蛾号,使得其簡(jiǎn)單易用,面向?qū)ο蟛僮餮难拧>€程的聲明周期由程序員管理鲜结,在實(shí)際開發(fā)中偶爾使用。
  3. GCD: 基于C語(yǔ)言的API活逆,充分利用設(shè)備的多核精刷,旨在替換NSThread等線程技術(shù)。線程的生命周期由系統(tǒng)自動(dòng)管理蔗候,在實(shí)際開發(fā)中經(jīng)常使用怒允。
  4. NSOperation: 基于OC語(yǔ)言API,底層是GCD锈遥,增加了一些更加簡(jiǎn)單易用的功能纫事,使用更加面向?qū)ο蟆>€程生命周期由系統(tǒng)自動(dòng)管理所灸,在實(shí)際開發(fā)中經(jīng)常使用丽惶。

二、單線程

進(jìn)程爬立,線程的概念就不在此文進(jìn)行介紹了钾唬。至于提到單線程,只是想跟大家聊一聊如果沒有多線程開發(fā)我們的項(xiàng)目將是怎么樣的侠驯。作為一個(gè)ios開發(fā)人員抡秆,大家都知道主線程是用來(lái)刷新UI的,而線程又是串行的吟策。也就是說(shuō)儒士,我們的程序運(yùn)行在主線程上,如果此時(shí)迎來(lái)了一些耗時(shí)操作的時(shí)候檩坚,我們的手機(jī)屏幕會(huì)卡住着撩,所以多線程開發(fā)是很必要的。本文的ThreadDemo工作組中有一個(gè)在主線程耗時(shí)操作的demo效床,名稱為TimeConsumingDemo睹酌。大家看一下就好了,demo很簡(jiǎn)單剩檀,寫在這里的目的也是希望能給讀者一個(gè)直觀的感受憋沿。

三、pthread

pthread這個(gè)方案在這里只為大家了解沪猴,可能很多l(xiāng)inux開發(fā)人員在使用這種方案進(jìn)行多線程開發(fā)操作辐啄。不過(guò)對(duì)于我來(lái)講在ios開發(fā)中我一次都沒用過(guò)采章。
使用pthread 要引入頭文件

#import <pthread.h>

然后創(chuàng)建線程

pthread_t thread = NULL;
id str = @"i'm pthread param";
pthread_create(&thread, NULL, operate, (__bridge void *)(str));

pthread_create的函數(shù)原型為

int pthread_create(pthread_t * __restrict, const pthread_attr_t * __restrict,
  void *(*)(void *), void * __restrict);

第一個(gè)參數(shù)pthread_t * __restrict
由于c語(yǔ)言沒有對(duì)象的概念,所以pthread_t實(shí)際是一個(gè)結(jié)構(gòu)體
所以創(chuàng)建的thread是一個(gè)指向當(dāng)前新建線程的指針

typedef __darwin_pthread_t pthread_t;
typedef struct _opaque_pthread_t *__darwin_pthread_t;
struct _opaque_pthread_t {
      long __sig;
      struct __darwin_pthread_handler_rec  *__cleanup_stack;
      char __opaque[__PTHREAD_SIZE__];
};

第二個(gè)參數(shù)const pthread_attr_t * __restrict
同樣是一個(gè)結(jié)構(gòu)體壶辜,這里是用來(lái)設(shè)置線程屬性的

typedef __darwin_pthread_attr_t pthread_attr_t;
typedef struct _opaque_pthread_attr_t __darwin_pthread_attr_t;
struct _opaque_pthread_attr_t {
    long __sig;
    char __opaque[__PTHREAD_ATTR_SIZE__];
};

第三個(gè)參數(shù)void ()(void *)這里給出了一個(gè)函數(shù)指針悯舟,指向的是一個(gè)函數(shù)的起始地址,所以是線程開啟后的回調(diào)函數(shù)砸民,這里demo給出的是operate函數(shù)抵怎,在線程中進(jìn)行耗時(shí)操作。
第四個(gè)參數(shù)是回調(diào)函數(shù)所用的參數(shù)

void *operate(void *param) {
    NSString *str = (__bridge NSString *)(param);
    // 用循環(huán)模擬耗時(shí)操作
    for (int i = 0; i < 100000; i++) {
        // [NSThread currentThread] 為獲取當(dāng)前
        NSLog(@"timeConsuming in %@, times: %d, param: %@", [NSThread currentThread], i, str);
    }
    pthread_exit((void*)0);
}

那么岭参,Pthreads在這里就介紹這么多反惕,要注意的一點(diǎn)是在使用Pthreads的時(shí)候一定要手動(dòng)把當(dāng)前線程結(jié)束掉。因?yàn)槲覀兘?jīng)常使用的GCD和NSOperation已經(jīng)被蘋果封裝過(guò)了演侯,所以我們想要定制進(jìn)行線程的操作就會(huì)有一定限制姿染,如果有想從底層進(jìn)行定制的讀者,可以去搜一下相關(guān)的資料秒际。

四悬赏、 NSThread

NSThread由蘋果進(jìn)行了封裝,并且完全面向?qū)ο舐病K钥梢灾苯邮褂肙C方法操控線程對(duì)象闽颇,非常直觀和方便∏独颍可以說(shuō)對(duì)于ios開發(fā)人員而言进萄,使用NSThread就開始了真正的多線程開發(fā)捻脖。所以锐峭,通過(guò)NSThread我們具體討論一些線程相關(guān)的問題,包括如下內(nèi)容:

  • 使用NSThread創(chuàng)建線程
  • 線程狀態(tài)
  • 線程間通信
  • 線程安全

1. 使用NSThread創(chuàng)建線程

使用NSThread創(chuàng)建線程有以下幾種方式:

  1. 使用NSThread的init方法顯式創(chuàng)建
  2. 使用NSThread類方法顯式創(chuàng)建并啟動(dòng)線程
  3. 隱式創(chuàng)建并啟動(dòng)線程

具體的代碼實(shí)現(xiàn)在下面已經(jīng)給出了可婶,這里提醒大家注意一點(diǎn)沿癞。只有使用NSThread的init方法創(chuàng)建的線程才會(huì)返回具體的線程實(shí)例。也就是說(shuō)如果想要對(duì)線程做更多的控制矛渴,比如添加線程的名字椎扬、更改優(yōu)先級(jí)等操作,要使用第一種方式來(lái)創(chuàng)建線程具温。但是此種方法需要使用start方法來(lái)手動(dòng)啟動(dòng)線程蚕涤。

/**
 *  隱式創(chuàng)建并啟動(dòng)線程
 */
- (void)createThreadWithImplicit {
    // 隱式創(chuàng)建并啟動(dòng)線程
    [self performSelectorInBackground:@selector(threadMethod3:) withObject:@"implicitMethod"];
}

/**
 *  使用NSThread類方法顯式創(chuàng)建并啟動(dòng)線程
 */
- (void)createThreadWithClassMethod {
    // 使用類方法創(chuàng)建線程并自動(dòng)啟動(dòng)線程
    [NSThread detachNewThreadSelector:@selector(threadMethod2:) toTarget:self withObject:@"fromClassMethod"];
}

/**
 *  使用init方法顯式創(chuàng)建線程
 */
- (void)createThreadWithInit {
    // 創(chuàng)建線程
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadMethod1) object:nil];
    // 設(shè)置線程名
    [thread1 setName:@"thread1"];
    // 設(shè)置優(yōu)先級(jí) 優(yōu)先級(jí)從0到1 1最高
    [thread1 setThreadPriority:0.9];
    // 啟動(dòng)線程
    [thread1 start];
}

2. 線程狀態(tài)

線程狀態(tài)分為:啟動(dòng)線程阻塞線程铣猩,結(jié)束線程
啟動(dòng)線程:

// 線程啟動(dòng)
- (void)start;

阻塞線程:

// 線程休眠到某一時(shí)刻
+ (void)sleepUntilDate:(NSDate *)date;
// 線程休眠多久
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

結(jié)束線程

// 結(jié)束線程
+ (void)exit;

大家在看官方api的時(shí)候可能會(huì)有一個(gè)疑問揖铜,api里明明有cancel方法,為什么使用cancel方法不能結(jié)束線程达皿?
當(dāng)我們使用cancel方法時(shí)天吓,只是改變了線程的狀態(tài)標(biāo)識(shí)贿肩,并不能結(jié)束線程,所以我們要配合isCancelled方法進(jìn)行使用龄寞。具體實(shí)現(xiàn)如下:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 創(chuàng)建線程
    [self createThread];
}

/**
 *  創(chuàng)建線程
 */
- (void)createThread {
    // 創(chuàng)建線程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMethod) object:nil];
    thread.name = @"i'm a new thread";
    // 啟動(dòng)線程
    [thread start];
}

/**
 *  線程方法
 */
- (void)threadMethod {
    NSLog(@"thread is create -- the name is: \"%@\"", [NSThread currentThread].name);
    // 線程阻塞 -- 延遲到某一時(shí)刻 --- 這里的時(shí)刻是3秒以后
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
    NSLog(@"sleep end");
    NSLog(@"sleep again");
    // 線程阻塞 -- 延遲多久 -- 這里延遲2秒
    [NSThread sleepForTimeInterval:2];
    NSLog(@"sleep again end");
    
    for (int i = 0 ; i < 100; i++) {
        NSLog(@"thread working");
        if(30 == i) {
            NSLog(@"thread will dead");
            [[NSThread currentThread] cancel];
        }
        if([[NSThread currentThread] isCancelled]) {
            // 結(jié)束線程
//            [NSThread exit];
            return;
        }
    }
}

3. 線程間通信

線程間通信我們最常用的就是開啟子線程進(jìn)行耗時(shí)操作汰规,操作完畢后回到主線程,進(jìn)行數(shù)據(jù)賦值以及刷新主線程UI物邑。在這里溜哮,用一個(gè)經(jīng)典的圖片下載demo進(jìn)行簡(jiǎn)述。
首先我們先了解一下api給出的線程間通信的方法:

//與主線程通信
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
  // equivalent to the first method with kCFRunLoopCommonModes

//與其他子線程通信
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);

以下是demo中的代碼片段:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 下載圖片
    [self downloadImage];
}

/**
 *  下載圖片
 */
- (void)downloadImage {
    // 創(chuàng)建線程下載圖片
    [NSThread detachNewThreadSelector:@selector(downloadImageInThread) toTarget:self withObject:nil];
}

/**
 *  線程中下載圖片操作
 */
- (void)downloadImageInThread {
    NSLog(@"come in sub thread -- %@", [NSThread currentThread]);
    // 獲取圖片url
    NSURL *url = [NSURL URLWithString:@"http://img.ycwb.com/news/attachement/jpg/site2/20110226/90fba60155890ed3082500.jpg"];
    // 計(jì)算耗時(shí)
    NSDate *begin = [NSDate date];
    // 使用CoreFoundation計(jì)算耗時(shí) CFDate
    CFTimeInterval beginInCF = CFAbsoluteTimeGetCurrent();
    // 從url讀取數(shù)據(jù)(下載圖片) -- 耗時(shí)操作
    NSData *imageData = [NSData dataWithContentsOfURL:url];
    NSDate *end = [NSDate date];
    CFTimeInterval endInCF= CFAbsoluteTimeGetCurrent();
    // 計(jì)算時(shí)間差
    NSLog(@"time difference -- %f", [end timeIntervalSinceDate:begin]);
    NSLog(@"time difference inCF -- %f", endInCF - beginInCF);
    // 通過(guò)二進(jìn)制data創(chuàng)建image
    UIImage *image = [UIImage imageWithData:imageData];
    
    // 回到主線程進(jìn)行圖片賦值和界面刷新
    [self performSelectorOnMainThread:@selector(backToMainThread:) withObject:image waitUntilDone:YES];
    // 這里也可以使用imageView的set方法進(jìn)行操作
//    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
}

/**
 *  回到主線程的操作
 */
- (void)backToMainThread:(UIImage *)image {
    NSLog(@"back to main thread --- %@", [NSThread currentThread]);
    // 賦值圖片到imageview
    self.imageView.image = image;
}

在demo中已經(jīng)把注釋寫的比較清晰了色解,需要補(bǔ)充的有三點(diǎn):

  1. performSelectorOnMainThread:withObject:waitUntilDone:方法這里是回到了主線程進(jìn)行操作茬射,同樣也可以使用
[self performSelector:@selector(backToMainThread:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];

回到主線程,或者進(jìn)入其他線程進(jìn)行操作冒签。

  1. 在實(shí)際項(xiàng)目中我們可能會(huì)分析耗時(shí)操作所花費(fèi)時(shí)間或者分析用戶行為的時(shí)候要計(jì)算用戶在當(dāng)前頁(yè)面所耗時(shí)間在抛,所以在demo中加入了時(shí)間的兩種計(jì)算方式,分別是CoreFoundation和Foundation中的萧恕。
// 計(jì)算耗時(shí)
    NSDate *begin = [NSDate date];
    // 使用CoreFoundation計(jì)算耗時(shí) CFDate
    CFTimeInterval beginInCF = CFAbsoluteTimeGetCurrent();
    // 從url讀取數(shù)據(jù)(下載圖片) -- 耗時(shí)操作
    NSData *imageData = [NSData dataWithContentsOfURL:url];
    NSDate *end = [NSDate date];
    CFTimeInterval endInCF= CFAbsoluteTimeGetCurrent();
    // 計(jì)算時(shí)間差
    NSLog(@"time difference -- %f", [end timeIntervalSinceDate:begin]);
    NSLog(@"time difference inCF -- %f", endInCF - beginInCF);
  1. 如果自己寫的項(xiàng)目無(wú)法運(yùn)行刚梭,可能是因?yàn)閄code7 創(chuàng)建HTTP請(qǐng)求報(bào)錯(cuò)導(dǎo)致,具體解決方案請(qǐng)點(diǎn)擊這里票唆。

4. 線程安全

因?yàn)槭嵌嗑€程操作朴读,所以會(huì)存在一定的安全隱患。原因是多線程會(huì)存在不同線程的資源共享走趋,也就是說(shuō)我們可能在同一時(shí)刻兩個(gè)線程同時(shí)操作了某一個(gè)變量的值衅金,但是線程的對(duì)變量的操作不同,導(dǎo)致變量的值出現(xiàn)誤差簿煌。下面是一個(gè)存取錢的demo片段:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    // 初始化狀態(tài)
    [self initStatus];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    // 啟動(dòng)線程
    [self startThread];
}

/**
 *  初始化狀態(tài)
 */
- (void)initStatus {
    // 設(shè)置存款
    self.depositMoney = 5000;
    // 創(chuàng)建存取錢線程
    self.saveThread = [[NSThread alloc] initWithTarget:self selector:@selector(saveAndDraw) object:nil];
    self.saveThread.name = @"save";
    self.drawThread = [[NSThread alloc] initWithTarget:self selector:@selector(saveAndDraw) object:nil];
    self.drawThread.name = @"draw";
}

/**
 *  開啟線程
 */
- (void)startThread {
    // 開啟存取錢線程
    [self.saveThread start];
    [self.drawThread start];
}

/**
 *  存取錢操作
 */
- (void)saveAndDraw {
    while(1) {
        if(self.depositMoney > 3000) {
            // 阻塞線程氮唯,模擬操作花費(fèi)時(shí)間
            [NSThread sleepForTimeInterval:0.05];
            if([[NSThread currentThread].name isEqualToString:@"save"]) {
                self.depositMoney += 100;
            } else {
                self.depositMoney -= 100;
            }
            NSLog(@"currentThread: %@, depositMoney: %d", [NSThread currentThread].name, self.depositMoney);
        } else {
            NSLog(@"no money");
            return;
        }
    }
}

在上面的demo中我們發(fā)現(xiàn),存取錢的線程是同時(shí)開啟的姨伟,而存取錢的錢數(shù)相同惩琉,所以每一次存取操作結(jié)束后,存款值應(yīng)該不會(huì)改變夺荒。大家可以運(yùn)行demo進(jìn)行查看結(jié)果瞒渠。
所以需要在線程操作中加入鎖:

/**
 *  存取錢操作
 */
- (void)saveAndDraw {
    while(1) {
        // 互斥鎖
        @synchronized (self) {
            if(self.depositMoney > 3000) {
               // 阻塞線程,模擬操作花費(fèi)時(shí)間
                [NSThread sleepForTimeInterval:0.05];
                if([[NSThread currentThread].name isEqualToString:@"save"]) {
                    self.depositMoney += 100;
                } else {
                    self.depositMoney -= 100;
                }
                NSLog(@"currentThread: %@, depositMoney: %d", [NSThread currentThread].name, self.depositMoney);
            } else {
                NSLog(@"no money");
                return;
            }
        }
    }
}

線程安全解決方案:

* 互斥鎖
@synchronized 的作用是創(chuàng)建一個(gè)互斥鎖技扼,保證此時(shí)沒有其它線程對(duì)鎖住的對(duì)象進(jìn)行修改伍玖。
* 互斥鎖使用格式:
@synchronized (鎖對(duì)象) { // 需要鎖定的代碼 }
* 互斥鎖的優(yōu)缺點(diǎn):
優(yōu)點(diǎn): 防止多線程對(duì)共享資源進(jìn)行搶奪造成的數(shù)據(jù)安全問題
缺點(diǎn): 需要消耗大量cpu資源

注:NSThread頭文件中的相關(guān)方法

//獲取當(dāng)前線程
 +(NSThread *)currentThread; 
//創(chuàng)建線程后自動(dòng)啟動(dòng)線程
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
//是否是多線程
+ (BOOL)isMultiThreaded;
//線程字典
- (NSMutableDictionary *)threadDictionary;
//線程休眠到什么時(shí)間
+ (void)sleepUntilDate:(NSDate *)date;
//線程休眠多久
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//退出線程
+ (void)exit;
//線程優(yōu)先級(jí)
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority NS_AVAILABLE(10_6, 4_0);
- (void)setThreadPriority:(double)p NS_AVAILABLE(10_6, 4_0);
//調(diào)用棧返回地址
+ (NSArray *)callStackReturnAddresses NS_AVAILABLE(10_5, 2_0);
+ (NSArray *)callStackSymbols NS_AVAILABLE(10_6, 4_0);
//設(shè)置線程名字
- (void)setName:(NSString *)n NS_AVAILABLE(10_5, 2_0);
- (NSString *)name NS_AVAILABLE(10_5, 2_0);
//獲取棧的大小
- (NSUInteger)stackSize NS_AVAILABLE(10_5, 2_0);
- (void)setStackSize:(NSUInteger)s NS_AVAILABLE(10_5, 2_0);
//是否是主線程
- (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0);
+ (BOOL)isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main
+ (NSThread *)mainThread NS_AVAILABLE(10_5, 2_0);
//初始化方法
- (id)init NS_AVAILABLE(10_5, 2_0); // designated initializer
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);
//是否正在執(zhí)行
- (BOOL)isExecuting NS_AVAILABLE(10_5, 2_0);
//是否執(zhí)行完成
- (BOOL)isFinished NS_AVAILABLE(10_5, 2_0);
//是否取消線程
- (BOOL)isCancelled NS_AVAILABLE(10_5, 2_0);
- (void)cancel NS_AVAILABLE(10_5, 2_0);
//線程啟動(dòng)
- (void)start NS_AVAILABLE(10_5, 2_0);
- (void)main NS_AVAILABLE(10_5, 2_0); // thread body method
@end
//多線程通知
FOUNDATION_EXPORT NSString * const NSWillBecomeMultiThreadedNotification;
FOUNDATION_EXPORT NSString * const NSDidBecomeSingleThreadedNotification;
FOUNDATION_EXPORT NSString * const NSThreadWillExitNotification;

@interface NSObject (NSThreadPerformAdditions)
//與主線程通信
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
  // equivalent to the first method with kCFRunLoopCommonModes
//與其他子線程通信
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
  // equivalent to the first method with kCFRunLoopCommonModes
//隱式創(chuàng)建并啟動(dòng)線程
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);

由于多線程內(nèi)容比較多,所以這里拆成兩個(gè)部分剿吻。此文介紹PThread和NSThread窍箍,下篇文章將會(huì)跟大家一起討論一下GCD和NSOperation的知識(shí)。本文中的代碼已經(jīng)上傳GitHub,希望本文章能對(duì)大家有所幫助仔燕。

請(qǐng)選擇紅框切換demo

由于示例demo比較多造垛,所以放到了workspace中,大家下載后在運(yùn)行的時(shí)候請(qǐng)選擇好demo晰搀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末五辽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子外恕,更是在濱河造成了極大的恐慌杆逗,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳞疲,死亡現(xiàn)場(chǎng)離奇詭異罪郊,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)尚洽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門悔橄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人腺毫,你說(shuō)我怎么就攤上這事癣疟。” “怎么了潮酒?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵睛挚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我急黎,道長(zhǎng)扎狱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任勃教,我火速辦了婚禮淤击,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荣回。我一直安慰自己遭贸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布心软。 她就那樣靜靜地躺著,像睡著了一般著蛙。 火紅的嫁衣襯著肌膚如雪删铃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天踏堡,我揣著相機(jī)與錄音猎唁,去河邊找鬼。 笑死顷蟆,一個(gè)胖子當(dāng)著我的面吹牛诫隅,可吹牛的內(nèi)容都是我干的腐魂。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼逐纬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蛔屹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起豁生,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤兔毒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后甸箱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體育叁,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年芍殖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豪嗽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡豌骏,死狀恐怖昵骤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肯适,我是刑警寧澤变秦,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站框舔,受9級(jí)特大地震影響蹦玫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刘绣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一樱溉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纬凤,春花似錦福贞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至恋技,卻和暖如春拇舀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜻底。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工骄崩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓要拂,卻偏偏與公主長(zhǎng)得像抠璃,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子脱惰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

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