iOS 多線程

iOS 多線程有幾種方式

  • GCD
  • NSOpeartion
  • NSThread
  • phread

多線程

GCD
  1. dispatch_once_t

    + (TestModel *)shared {
        static TestModel *model;
        static dispatch_once_t once;
        dispatch_once(&once, ^{
            model = [[TestModel alloc] init];
        });
        return model;
    }
    
  2. dispatch_group

    - (void)dispatchGroup {
        dispatch_queue_t queue = dispatch_queue_create("groupTest", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group = dispatch_group_create();
    
        dispatch_group_async(group, queue, ^{
            NSLog(@"任務(wù)1 ready");
            sleep(2);
            NSLog(@"任務(wù)1 完成");
        });
         dispatch_group_enter(group);
        dispatch_async(queue, ^{
            NSLog(@"任務(wù)2 ready");
            sleep(4);
            NSLog(@"任務(wù)2 完成");
            dispatch_group_leave(group);
        });
        dispatch_group_notify(group, queue, ^{
            NSLog(@"group 任務(wù)完成");
        });
    }
    
    • dispatch_group_notify 可以添加多次领铐,并會(huì)多次調(diào)用
    • 只要 group 中的任務(wù)沒有完成,group 完成的監(jiān)聽就不會(huì)被調(diào)用稿饰,即使是后追加的任務(wù)
    • notify 方法中第二個(gè) queue 的參數(shù)決定了 callBack 將會(huì)在那個(gè)隊(duì)列執(zhí)行
  3. dispatch_apply 多線程快速遍歷

    • 本質(zhì)是 dispatch_syncdispatch_group 關(guān)聯(lián)的 api,因?yàn)樵摲椒〞?huì)等待內(nèi)部所有操作都結(jié)束再返回,內(nèi)部操作是否同步依賴傳入的queue寥掐,外部必定是同步的最岗。如果有需要,需將該方法放到一個(gè)異步的并行隊(duì)列中

    • 如果傳入多線程荆永,輸出的下標(biāo)未必按照順序執(zhí)行

    - (void)dispatchApply {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_apply(10, queue, ^(size_t index) {
            NSLog(@"%@------%d", NSThread.currentThread, index);
            sleep(1);
        });
         NSLog(@"111111");
    }
    // "111111" 必定出現(xiàn)在最后
    
  4. 定時(shí)器 DispatchSourceTimer

    不同于基于 Runloop 的 NSTimer废亭,DispatchSourceTimer 不會(huì)因?yàn)樽泳€程沒有正在運(yùn)行的 Runloop 而失效,也不會(huì)有循環(huán)引用具钥、計(jì)時(shí)不準(zhǔn)(每次 runloop 循環(huán)才會(huì)檢查定時(shí)器是否需要被執(zhí)行)等問題豆村。但有幾點(diǎn)需要注意:

    • suspend 與 resume 一定要成對(duì)使用,否則會(huì) crash
    • timer 最好被持有骂删,否則在 suspend 時(shí)可能 crash
    var timer: DispatchSourceTimer?
    var index: Int = 0
    func startTimer() {
        timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.global())
        timer?.schedule(deadline: .now(), repeating: 0.1)
        timer?.setEventHandler(handler: {
            print("index: \(index)")
            index += 1
            if index == 10 {
                cancelTimer()
            }
        })
        timer?.resume()
    }
    
    private func cancelTimer() {
        timer?.cancel()
        timer = nil
    }
    
  5. 信號(hào)量 DispatchSemaphore

    信號(hào)量類似于鎖掌动,信號(hào)量為 0 則阻塞線程,大于 0 則不會(huì)阻塞宁玫。因此可以通過改變信號(hào)量的值來控制是否阻塞線程

    • DispatchSemaphore(value: 0) 初始化
    • semaphore.signal() // 信號(hào)量 +1
    • semaphore.wait() 在信號(hào)量大于 0 的前提下粗恢,信號(hào)量 -1,如果信號(hào)量本來為 0欧瘪,則線程休眠眷射,加入到等待這個(gè)信號(hào)的線程隊(duì)列當(dāng)中。當(dāng)信號(hào)量大于 0 時(shí)佛掖,就會(huì)喚醒這個(gè)等待隊(duì)列中靠前的線程妖碉,繼續(xù)線程后面的代碼且對(duì)信號(hào)量減 1,也就確保了信號(hào)量大于 0 才減 1苦囱,所以不存在信號(hào)量小于 0 的情況(除非在初始化時(shí)設(shè)置為負(fù)數(shù)嗅绸,不過這樣做應(yīng)用程序會(huì) crash)。
    // 下載兩個(gè)圖片后執(zhí)行操作 A
    // 1. 初始化信號(hào)量為 0
    var seamphore = DispatchSemaphore(value: -0)
    // 2. 操作 A 前添加兩個(gè) wait 操作
    seamphore.wait()
    seamphore.wait()
    // 3. 在每個(gè)下載結(jié)果回調(diào)中添加
    seamphore.signal()
    
  6. 線程?hào)艡? dispatch_barrier

    線程?hào)艡诳梢宰枞硞€(gè) queue(必須是自定義的并行 queue撕彤,如果是 global 鱼鸠,則不會(huì)有預(yù)期效果)中任務(wù)的執(zhí)行直到 queue 中柵欄之前的任務(wù)執(zhí)行完畢

    - (void)dispatchBarrier {
        dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
        for (int i = 1; i <= 3; i ++) {
            dispatch_async(queue, ^{
                sleep(1);
                NSLog(@"%d 任務(wù)結(jié)束", i);
            });
        }
        NSLog(@"柵欄前面");
        dispatch_barrier_sync(queue, ^{
            sleep(3);
            NSLog(@"柵欄結(jié)束");
        });
        NSLog(@"柵欄后面");
        for (int i = 4; i <= 6; i ++) {
            dispatch_async(queue, ^{
                sleep(1);
                NSLog(@"%d 任務(wù)結(jié)束", i);
            });
        }
        NSLog(@"代碼結(jié)束");
    }
    

    運(yùn)行結(jié)果

    dispatch_barrier_sync 運(yùn)行結(jié)果
    2020-07-24 20:17:09.905062+0800 ObjcTest[13685:969878] 柵欄前面
    2020-07-24 20:17:10.907255+0800 ObjcTest[13685:969907] 2 任務(wù)結(jié)束
    2020-07-24 20:17:10.907310+0800 ObjcTest[13685:969908] 1 任務(wù)結(jié)束
    2020-07-24 20:17:10.907579+0800 ObjcTest[13685:969906] 3 任務(wù)結(jié)束
    2020-07-24 20:17:13.907888+0800 ObjcTest[13685:969878] 柵欄結(jié)束
    2020-07-24 20:17:13.908349+0800 ObjcTest[13685:969878] 柵欄后面
    2020-07-24 20:17:13.908954+0800 ObjcTest[13685:969878] 代碼結(jié)束
    2020-07-24 20:17:14.913851+0800 ObjcTest[13685:969906] 4 任務(wù)結(jié)束
    2020-07-24 20:17:14.914364+0800 ObjcTest[13685:969908] 5 任務(wù)結(jié)束
    2020-07-24 20:17:14.914628+0800 ObjcTest[13685:969907] 6 任務(wù)結(jié)束
    dispatch_barrier_async 運(yùn)行結(jié)果
    2020-07-24 20:18:16.955921+0800 ObjcTest[13691:970322] 柵欄前面
    2020-07-24 20:18:16.956002+0800 ObjcTest[13691:970322] 柵欄后面
    2020-07-24 20:18:16.956040+0800 ObjcTest[13691:970322] 代碼結(jié)束
    2020-07-24 20:18:17.961793+0800 ObjcTest[13691:970352] 1 任務(wù)結(jié)束
    2020-07-24 20:18:17.962022+0800 ObjcTest[13691:970353] 2 任務(wù)結(jié)束
    2020-07-24 20:18:17.963561+0800 ObjcTest[13691:970357] 3 任務(wù)結(jié)束
    2020-07-24 20:18:20.967381+0800 ObjcTest[13691:970357] 柵欄結(jié)束
    2020-07-24 20:18:21.974364+0800 ObjcTest[13691:970357] 4 任務(wù)結(jié)束
    2020-07-24 20:18:21.974901+0800 ObjcTest[13691:970353] 5 任務(wù)結(jié)束
    2020-07-24 20:18:21.975174+0800 ObjcTest[13691:970352] 6 任務(wù)結(jié)束
    

    dispatch_barrier_syncdispatch_barrier_async 區(qū)別為

    • 同步柵欄會(huì)阻塞之后的普通代碼的執(zhí)行猛拴,異步柵欄則不會(huì)

    應(yīng)用線程?hào)艡诘奶匦裕梢愿玫淖鲆恍┚€程同步蚀狰。例如

    任務(wù) 1愉昆、2、3結(jié)束后需要執(zhí)行任務(wù) 4麻蹋、5跛溉、6

    如果用 dispatch_group ,則將任務(wù) 1扮授、2芳室、3 添加到 group 中,在 dispatch_group_notify 中執(zhí)行任務(wù) 4刹勃、5堪侯、6

NSOperation

NSOperation 是蘋果 GCD 面向?qū)ο蟮姆庋b

優(yōu)點(diǎn):

  • 添加操作間的依賴關(guān)系,控制執(zhí)行順序

  • 可以方便的取消一個(gè)操作的執(zhí)行

  • 設(shè)定操作的優(yōu)先級(jí)

  • 使用 KVO 觀察操作執(zhí)行狀態(tài)的更改

  1. NSOperation 執(zhí)行的方式

    • 添加到 NSOperationQueue荔仁,系統(tǒng)會(huì)自動(dòng)將 NSOperationQueue 中的 NSOperation 取出來伍宦,在線程中執(zhí)行操作
    • 如果不顯式將 NSOperation 添加到 NSOperationQueue 中,也可以調(diào)用 operation.start() 方法乏梁,操作會(huì)在當(dāng)前線程中執(zhí)行

    備注:使用 start 方法時(shí)次洼,如果 NSBlockOperation 調(diào)用 addExecutionBlock:方法,添加額外的操作遇骑,包括 blockOperationWithBlock 中的操作在內(nèi)的這些操作可能在不同的線程中并發(fā)執(zhí)行卖毁,具體由系統(tǒng)決定

        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 3; i ++) {
                sleep(2);
                NSLog(@"1---%@", NSThread.currentThread);
            }
        }];
        [op addExecutionBlock:^{
            for (int i = 0; i < 3; i ++) {
                sleep(2);
                NSLog(@"2---%@", NSThread.currentThread);
            }
        }];
        [op addExecutionBlock:^{
            for (int i = 0; i < 3; i ++) {
                sleep(2);
                NSLog(@"3---%@", NSThread.currentThread);
            }
        }];
        [op addExecutionBlock:^{
            for (int i = 0; i < 3; i ++) {
                sleep(2);
                NSLog(@"4---%@", NSThread.currentThread);
            }
        }];
        [op addExecutionBlock:^{
            for (int i = 0; i < 3; i ++) {
                sleep(2);
                NSLog(@"5---%@", NSThread.currentThread);
            }
        }];
        [op addExecutionBlock:^{
            for (int i = 0; i < 3; i ++) {
                sleep(2);
                NSLog(@"6---%@", NSThread.currentThread);
            }
        }];
        [op start];
    
    2020-07-27 14:19:25.237520+0800 ObjcTest[19476:1527007] 1---<NSThread: 0x282870ac0>{number = 5, name = (null)}
    2020-07-27 14:19:25.237532+0800 ObjcTest[19476:1526976] 2---<NSThread: 0x28283da80>{number = 1, name = main}
    2020-07-27 14:19:27.239170+0800 ObjcTest[19476:1526976] 2---<NSThread: 0x28283da80>{number = 1, name = main}
    2020-07-27 14:19:27.239164+0800 ObjcTest[19476:1527007] 1---<NSThread: 0x282870ac0>{number = 5, name = (null)}
    2020-07-27 14:19:29.240627+0800 ObjcTest[19476:1526976] 2---<NSThread: 0x28283da80>{number = 1, name = main}
    2020-07-27 14:19:29.240620+0800 ObjcTest[19476:1527007] 1---<NSThread: 0x282870ac0>{number = 5, name = (null)}
    2020-07-27 14:19:31.242202+0800 ObjcTest[19476:1527007] 4---<NSThread: 0x282870ac0>{number = 5, name = (null)}
    

    從結(jié)果中可以看出 blockOperationWithBlock 中的代碼不是在主線程中執(zhí)行的,并且系統(tǒng)一共開啟了兩個(gè)線程執(zhí)行該 Operation

  2. NSOperationQueue 控制串行质蕉、并發(fā)

    maxConcurrentOperationCount 屬性可以控制一個(gè)隊(duì)列中可以有多少個(gè)操作同時(shí)參與并發(fā)執(zhí)行

    注意:這里的 maxConcurrentOperationCount 控制的不是并發(fā)線程的數(shù)量势篡,而是一個(gè)隊(duì)列中同時(shí)能并發(fā)執(zhí)行的最大操作數(shù)。而且一個(gè)操作也并非只能在一個(gè)線程中運(yùn)行模暗。

  3. NSOperation 操作依賴

    NSOperation 可以添加操作之間的依賴禁悠。因此我們可以很方便的控制操作之間的執(zhí)行先后順序。并且一個(gè)操作可以添加多個(gè)依賴兑宇。

    例如我們有如下需求:A碍侦、B兩個(gè)操作,A 執(zhí)行完畢隶糕,B 才能執(zhí)行操作瓷产。

    // 1.創(chuàng)建隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 2.創(chuàng)建操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
             [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
             NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 2; i++) {
            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
        }
    }];
    
    // 3.添加依賴
    [op2 addDependency:op1]; // 讓op2 依賴于 op1,則先執(zhí)行op1枚驻,在執(zhí)行op2
    
    // 4.添加操作到隊(duì)列中
    [queue addOperation:op1];
    [queue addOperation:op2];
    

    輸出如下

    2020-07-27 14:43:35.998300+0800 ObjcTest[19617:1533985] 1---<NSThread: 0x281496200>{number = 6, name = (null)}
    2020-07-27 14:43:38.000611+0800 ObjcTest[19617:1533985] 1---<NSThread: 0x281496200>{number = 6, name = (null)}
    2020-07-27 14:43:40.007131+0800 ObjcTest[19617:1533985] 2---<NSThread: 0x281496200>{number = 6, name = (null)}
    2020-07-27 14:43:42.021944+0800 ObjcTest[19617:1533985] 2---<NSThread: 0x281496200>{number = 6, name = (null)}
    

    可以看出濒旦,op1 先執(zhí)行完畢后,op2 才開始執(zhí)行

  4. NSOperation 優(yōu)先級(jí)

    優(yōu)先級(jí)只體現(xiàn)在兩個(gè)時(shí)間點(diǎn)

    • 依賴任務(wù)處理完成再登、隊(duì)列對(duì)后續(xù)任務(wù)的調(diào)度
    • 依賴隊(duì)列從暫停轉(zhuǎn)變?yōu)橹匦聠?dòng)尔邓、后續(xù)任務(wù)的調(diào)度
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"1--- 開始執(zhí)行, %@", NSThread.currentThread);
            sleep(2);
            NSLog(@"1--- 執(zhí)行完畢");
        }];
        op1.queuePriority = NSOperationQueuePriorityLow;
    
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"2--- 開始執(zhí)行, %@", NSThread.currentThread);
            sleep(2);
            NSLog(@"2--- 執(zhí)行完畢");
        }];
        op2.queuePriority = NSOperationQueuePriorityHigh;
    
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"依賴--- 開始執(zhí)行, %@", NSThread.currentThread);
            sleep(2);
            NSLog(@"依賴--- 執(zhí)行完畢");
        }];
        [op1 addDependency:op3];
        [op2 addDependency:op3];
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        queue.maxConcurrentOperationCount = 1;
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
    

    結(jié)果如下

    2020-07-27 15:13:05.623639+0800 ObjcTest[19750:1540177] 依賴--- 開始執(zhí)行, <NSThread: 0x281561c00>{number = 5, name = (null)}
    2020-07-27 15:13:07.629611+0800 ObjcTest[19750:1540177] 依賴--- 執(zhí)行完畢
    2020-07-27 15:13:07.630799+0800 ObjcTest[19750:1540177] 2--- 開始執(zhí)行, <NSThread: 0x281561c00>{number = 5, name = (null)}
    2020-07-27 15:13:09.631681+0800 ObjcTest[19750:1540177] 2--- 執(zhí)行完畢
    2020-07-27 15:13:09.632164+0800 ObjcTest[19750:1540176] 1--- 開始執(zhí)行, <NSThread: 0x281561380>{number = 3, name = (null)}
    2020-07-27 15:13:11.637458+0800 ObjcTest[19750:1540176] 1--- 執(zhí)行完畢
    
  5. NSOperationQueue 暫停和取消

    注意:

    1. 這里的暫停和取消(包括操作的取消和隊(duì)列的取消)并不代表可以將當(dāng)前的操作立即取消晾剖,而是當(dāng)當(dāng)前的操作執(zhí)行完畢后不再執(zhí)行新的操作
    2. 暫停和取消的區(qū)別在于:暫停之后還可以恢復(fù)操作,繼續(xù)向下執(zhí)行梯嗽;而取消操作之后齿尽,所有的操作就清空了,不再執(zhí)行剩下的操作
NSThread

NSThread 在代碼中偶爾會(huì)使用灯节,例如 [NSThread currentThread]

  1. 創(chuàng)建循头、啟動(dòng)線程

    - (void)threadTest {
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
        [thread start];
      // 創(chuàng)建線程后自動(dòng)啟動(dòng)線程
      //[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
    
      // 隱式創(chuàng)建并啟動(dòng)線程
      //[self performSelectorInBackground:@selector(run) withObject:nil];
    }
    
    - (void)run {
        NSLog(@"currentThread: %@", NSThread.currentThread);
    }
    
  2. 線程狀態(tài)控制

    + (void)sleepUntilDate:(NSDate *)date;
    + (void)sleepForTimeInterval:(NSTimeInterval)ti;// 線程進(jìn)入阻塞狀態(tài)
    + (void)exit; // 線程 kill
    
  3. 線程間通信

    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
    
  4. 與 runloop 關(guān)系

    - (void)run {
        NSLog(@"currentThread: %@", NSThread.currentThread);
        [self performSelector:@selector(threadAfter) withObject:nil afterDelay:1];
    }
    
    - (void)threadAfter {
        NSLog(@"1111111");
    }
    

    上述代碼默認(rèn)不會(huì)起作用,‘1111111’ 不會(huì)被打印出來炎疆。 performSelector: withObject: afterDelay: 方法默認(rèn)會(huì)創(chuàng)建一個(gè) timer 添加到當(dāng)前 runloop 中卡骂,而子線程默認(rèn)不開啟 runloop,因此上述代碼不起作用形入。
    解決方法是在 performSelector 方法后面添加[[NSRunLoop currentRunLoop] run];
    如果將 runloop 啟動(dòng)的代碼放到前面偿警,仍然不會(huì)起作用,原因是 runloop 啟動(dòng)后沒有可執(zhí)行的代碼唯笙,會(huì)立刻退出,此時(shí)再添加 timer 也沒有什么作用盒使。

pthread

pthread 是一套通用的多線程 API崩掘,使用 C 語言編寫,需要程序員自己管理線程的生命周期少办,使用難度較大苞慢,很少使用

  • pthread_create()創(chuàng)建一個(gè)線程
  • pthread_exit()終止當(dāng)前線程
  • pthread_cancel()中斷另外一個(gè)線程的運(yùn)行
  • pthread_join()阻塞當(dāng)前的線程哀墓,直到另外一個(gè)線程運(yùn)行結(jié)束
  • pthread_attr_init()初始化線程的屬性
  • pthread_attr_setdetachstate()設(shè)置脫離狀態(tài)的屬性(決定這個(gè)線程在終止時(shí)是否可以被結(jié)合)
  • pthread_attr_getdetachstate()獲取脫離狀態(tài)的屬性
  • pthread_attr_destroy()刪除線程的屬性
  • pthread_kill()向線程發(fā)送一個(gè)信號(hào)

問題

  1. 子線程同時(shí)執(zhí)行 ABC 三個(gè)同步任務(wù)筐高,全部執(zhí)行完畢后再在自線程執(zhí)行三個(gè)同步任務(wù) EDF,應(yīng)該怎樣做骡技?
    • 可以使用 GCD 的group 或者 NSOperation 的 依賴
    • 可以使用 dispatch_barrier 稍簡(jiǎn)單一些
  2. 將問題 1 中的 ABC 三個(gè)任務(wù)改為異步任務(wù)如 AFN 網(wǎng)絡(luò)請(qǐng)求蔓纠,全部回調(diào)成功后進(jìn)行數(shù)據(jù)整合辑畦,應(yīng)該怎樣做?
    1. 使用信號(hào)量
    2. 使用 GCD 的 group 也可以腿倚。dispatch_group_enterdispatch_group_leave 搭配使用
  3. 線程的生命周期是怎樣的纯出?
    可參考 線程的生命周期以及常駐線程

參考

  1. iOS 多線程:『NSOperation、NSOperationQueue』詳盡總結(jié)
  2. iOS基礎(chǔ)深入補(bǔ)完計(jì)劃--多線程(面試題)匯總
  3. 深入淺出 GCD 之 dispatch_group
  4. iOS多線程相關(guān)面試題
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末敷燎,一起剝皮案震驚了整個(gè)濱河市暂筝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌硬贯,老刑警劉巖焕襟,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異饭豹,居然都是意外死亡鸵赖,警方通過查閱死者的電腦和手機(jī)务漩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卫漫,“玉大人菲饼,你說我怎么就攤上這事×惺辏” “怎么了宏悦?”我有些...
    開封第一講書人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)包吝。 經(jīng)常有香客問我饼煞,道長(zhǎng),這世上最難降的妖魔是什么诗越? 我笑而不...
    開封第一講書人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任砖瞧,我火速辦了婚禮,結(jié)果婚禮上嚷狞,老公的妹妹穿的比我還像新娘块促。我一直安慰自己,他們只是感情好床未,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開白布竭翠。 她就那樣靜靜地躺著,像睡著了一般薇搁。 火紅的嫁衣襯著肌膚如雪斋扰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,549評(píng)論 1 312
  • 那天啃洋,我揣著相機(jī)與錄音传货,去河邊找鬼。 笑死宏娄,一個(gè)胖子當(dāng)著我的面吹牛问裕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播孵坚,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼僻澎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了十饥?” 一聲冷哼從身側(cè)響起窟勃,我...
    開封第一講書人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逗堵,沒想到半個(gè)月后秉氧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜒秤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年汁咏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亚斋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攘滩,死狀恐怖帅刊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漂问,我是刑警寧澤赖瞒,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站蚤假,受9級(jí)特大地震影響栏饮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜磷仰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一袍嬉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧灶平,春花似錦伺通、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拼苍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間调缨,已是汗流浹背疮鲫。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弦叶,地道東北人俊犯。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像伤哺,于是被迫代替她去往敵國(guó)和親燕侠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361