iOS多線程實現(xiàn)方案之 -- GCD

昨天通過多線程實現(xiàn)方案之 -- NSThread說了關于 NSThread 多線程的一些知識點和用法, 其實之前我也寫過一篇關于 GCD 的分享-iOS - GCD 編程, 使用的 GCD 是基于封裝過的, 今天是深入學習總結 GCD 相關知識以及 GCD 在實際開發(fā)中的使用

什么是 GCD

  • 全稱是 Grand Central Dispatch
  • 純 C語言, 提供了非常強大的函數(shù)

GCD 的優(yōu)勢

  • GCD 是蘋果公司為多核的并發(fā)運算提出的解決方案
  • GCD 會自動利用更多的 CPU 內(nèi)核(比如雙核, 四核)
  • GCD 會自動管理線程的生命周期, (創(chuàng)建線程, 調(diào)度任務, 銷毀線程)
  • 程序員只需要告訴 GCD 需要執(zhí)行什么任務, 不需要編寫任何線程管理代碼

任務和隊列

GCD 中有兩個核心概念

  • 任務: 執(zhí)行什么操作
  • 隊列: 用來仿什么任務

GCD 使用的兩個步驟

  1. 定制任務

    • 確定想要做的事情
  2. 講任務添加到隊列中

    • GCD 會自動將隊列中的任務取出, 放到對應的線程中執(zhí)行
    • 任務的取出遵循隊列的 FIFO 原則: 先進先出, 后進后出

執(zhí)行任務

GCD 中有兩個用來執(zhí)行任務的常用函數(shù)

  • 用同步的方式執(zhí)行任務
dispatch_sync(dispatch_queue_t queue, ^{
        // block 內(nèi)容
    });

queue: 隊列
bolck: 任務

  • 用異步方式來執(zhí)行
dispatch_async(dispatch_queue_t queue, ^{
        // block 內(nèi)容
    });

同步和異步的區(qū)別

  • 同步: 只能在當前線程中執(zhí)行任務, 不具備開新啟線程的能力
  • 異步: 可以在新的線程中執(zhí)行任務, 具備開啟新線程的能力

隊列的類型

并發(fā)隊列(Concurrent Dispatch Queue)

概念:

  • 可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)
  • 并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效

創(chuàng)建方法:

dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_CONCURRENT);

因為 GCD 默認已經(jīng)提供了全局并發(fā)隊列, 供整個應用使用, 也可以直接獲取

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 第一個參數(shù)是隊列優(yōu)先級, 第二個參數(shù)一般都是0, 沒什么用

串行隊列(Serial Dispatch Queue)

概念:

  • 讓任務一個接著一個的執(zhí)行 (一個任務執(zhí)行完畢再執(zhí)行下一個任務)

創(chuàng)建方式:

// 第二個隊列類型可以傳 NULL 或者 DISPATCH_QUEUE_SERIAL 效果是一樣的
dispatch_queue_t queue = dispatch_queue_create("abc", DISPATCH_QUEUE_SERIAL);

還可以使用主隊列, 也就是跟主線程想關聯(lián)的隊列

  • 主隊列是 GCD 自帶的一種特殊串行隊列
  • 放在主隊列中的任務, 多會放到主線程中執(zhí)行
  • 使用dispatch_get_main_queue()獲得主隊列

容易混淆的術語

在 GCD 使用的時候有4個概念比較容易混淆: 同步 - 異步 - 并發(fā) - 串行

  • 同步和異步主要影響: 能不能開啟新線程

    • 同步: 只是在當前線程中執(zhí)行任務, 不具備開啟新線程的能力
    • 異步: 可以在新的線程中執(zhí)行任務, 具備開啟新線程的能力
  • 并發(fā)和串行主要影響: 任務的執(zhí)行方式

    • 并發(fā): 允許多個任務并發(fā)執(zhí)行
    • 串行: 一個任務執(zhí)行完畢, 才去執(zhí)行下一個任務

GCD 的基本使用

同步函數(shù)和并發(fā)隊列

直接上代碼

- (void)syncConcurrent {
    
    // 創(chuàng)建隊列
    /*
     第一個參數(shù): C語言的字符串,標簽
     第二個參數(shù): 隊列的類型
     */
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
    
    // 定制任務(多任務)
    dispatch_sync(queue, ^{
        
        NSLog(@"download1- %@", [NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download2- %@", [NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download3- %@", [NSThread currentThread]);
        
    });   
}

打印結果:

2016-07-28 11:14:52.766 GCD[18620:1367714] download1- <NSThread: 0x7f9ee8e014b0>{number = 1, name = main}
2016-07-28 11:14:52.767 GCD[18620:1367714] download2- <NSThread: 0x7f9ee8e014b0>{number = 1, name = main}
2016-07-28 11:14:52.770 GCD[18620:1367714] download3- <NSThread: 0x7f9ee8e014b0>{number = 1, name = main}

同步函數(shù)是不會開啟子線程的, 所有任務都是在主線程中串行執(zhí)行的.

同步函數(shù)和串行隊列

代碼:

- (void)syncSerial {
    
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download1- %@", [NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download2- %@", [NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download3- %@", [NSThread currentThread]);
        
    });
    
}

打印結果:

2016-07-28 11:18:56.510 GCD[18829:1370325] download1- <NSThread: 0x7fe08bf00af0>{number = 1, name = main}
2016-07-28 11:18:56.511 GCD[18829:1370325] download2- <NSThread: 0x7fe08bf00af0>{number = 1, name = main}
2016-07-28 11:18:56.513 GCD[18829:1370325] download3- <NSThread: 0x7fe08bf00af0>{number = 1, name = main}

同樣, 此時也是不會創(chuàng)建子線程的, 所有任務是在主線程中也是串行執(zhí)行, 和同步函數(shù)和并發(fā)隊列時候是一樣的效果.

同步函數(shù)和主隊列

這個就有點特殊了, 為了看效果, 在方法里面加上開始和結束的代碼:

- (void)syncMain {
    
    NSLog(@"---start---");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download1- %@", [NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download2- %@", [NSThread currentThread]);
        
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"download3- %@", [NSThread currentThread]);
        
    });
    
    NSLog(@"---end---");
    
}

打印結果:

2016-07-28 12:02:20.473 GCD[21183:1395248] ---start---

并沒有打印結束語句, 說明任務也沒有執(zhí)行,這是怎么回事呢?

  • 這是因為主列發(fā)中的任務都是在主線程中執(zhí)行, 當主隊列發(fā)現(xiàn)當前主線程有任務在執(zhí)行, 那主隊列會暫定調(diào)用對隊列中的任務,直到主線程空閑為止.
  • 簡單點說, 就是主線程發(fā)現(xiàn)有任務, 就要讓主線程去執(zhí)行任務, 但此時的主線程卻在等待這任務執(zhí)行完畢, 不是空閑狀態(tài), 所以主線程無法執(zhí)行任務, 形成死鎖. 而同步函數(shù)又要求任務要立刻馬上按順序執(zhí)行, 所以第一個任務執(zhí)行不了, 后面的當然也執(zhí)行不了 , 就卡在了那里.

那有沒有辦法讓同步函數(shù)和主隊列中的任務執(zhí)行呢? 當然可以, 只是需要把這個方法放到子線程中去, 看代碼:

[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];

這個時候再看執(zhí)行結果:

2016-07-28 12:17:01.477 GCD[21971:1403347] ---start---
2016-07-28 12:17:01.481 GCD[21971:1403123] download1- <NSThread: 0x7fca2bc02470>{number = 1, name = main}
2016-07-28 12:17:01.500 GCD[21971:1403123] download2- <NSThread: 0x7fca2bc02470>{number = 1, name = main}
2016-07-28 12:17:01.503 GCD[21971:1403123] download3- <NSThread: 0x7fca2bc02470>{number = 1, name = main}
2016-07-28 12:17:01.504 GCD[21971:1403347] ---end---

發(fā)現(xiàn)已經(jīng)全部執(zhí)行完畢了, 而且是在主線程中執(zhí)行的. 這是因為我們是開啟的子線程來調(diào)用方法, 此時的主線程是空閑的, 然后方法中的任務需要在主線程中執(zhí)行, 就沒有問題了.

異步函數(shù)和并發(fā)隊列

定制三個任務, 看執(zhí)行效果

- (void)asyncConcurrent {
    
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
    
   // 也可以獲取全局并發(fā)隊列,執(zhí)行效果是一樣的
    // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    
    dispatch_async(queue, ^{
       
        NSLog(@"download1- %@", [NSThread currentThread]);
        
    });
    dispatch_async(queue, ^{
       
        NSLog(@"download2- %@", [NSThread currentThread]);
        
    });
    dispatch_async(queue, ^{
       
        NSLog(@"download3- %@", [NSThread currentThread]);
        
    });
}

打印結果:

2016-07-28 10:58:05.941 GCD[17694:1357547] download1- <NSThread: 0x7f9c5b6155e0>{number = 2, name = (null)}
2016-07-28 10:58:05.943 GCD[17694:1357551] download3- <NSThread: 0x7f9c5b551df0>{number = 4, name = (null)}
2016-07-28 10:58:05.942 GCD[17694:1357550] download2- <NSThread: 0x7f9c5b548b30>{number = 3, name = (null)}

可以看出隊列開啟了三條子線程區(qū)分別執(zhí)行三個任務, 隊列中的任務是并發(fā)執(zhí)行的. 但是在這里有個注意點:

并不是說有多少任務GCD 就會開啟多少條線程, 具體開啟幾條線程是不確定的, 這個是由系統(tǒng)決定的.

異步函數(shù)和串行隊列

同樣是三個任務,看執(zhí)行效果

- (void)asyncSerial {
    
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        
        NSLog(@"download1- %@", [NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"download2- %@", [NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"download3- %@", [NSThread currentThread]);
        
    });
}

打印結果

2016-07-28 11:08:08.933 GCD[18241:1363508] download1- <NSThread: 0x7f80c0f11330>{number = 2, name = (null)}
2016-07-28 11:08:08.934 GCD[18241:1363508] download2- <NSThread: 0x7f80c0f11330>{number = 2, name = (null)}
2016-07-28 11:08:08.934 GCD[18241:1363508] download3- <NSThread: 0x7f80c0f11330>{number = 2, name = (null)}

隊列只開啟了一條子線程, 去一個接著一個任務去執(zhí)行.
這種方式對任務的執(zhí)行效率沒有任何提高.

異步函數(shù)和主隊列

代碼:

- (void)asyncMain {
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        
        NSLog(@"download1- %@", [NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"download2- %@", [NSThread currentThread]);
        
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"download3- %@", [NSThread currentThread]);
        
    });
    
}

打印結果:

2016-07-28 11:55:12.710 GCD[20775:1389553] download1- <NSThread: 0x7fc4ea7033b0>{number = 1, name = main}
2016-07-28 11:55:12.712 GCD[20775:1389553] download2- <NSThread: 0x7fc4ea7033b0>{number = 1, name = main}
2016-07-28 11:55:12.712 GCD[20775:1389553] download3- <NSThread: 0x7fc4ea7033b0>{number = 1, name = main}

主隊列所有的任務確實是在主線程執(zhí)行的, 雖然是異步函數(shù), 但也不會開啟線程.

各種隊列執(zhí)行效果總結

直接在 Excel 里做了個表


總結: GCD 里, 非主隊列情況下只有異步函數(shù)才會開啟新線程, 此時如果是并發(fā)隊列, 會開啟多條線程,如果是串行隊列, 只會開啟一條線程, 其他情況下(包括主隊列) 都不會開啟新線程,并且是串行執(zhí)行任務.

GCD 線程間通信

GCD 線程間通信相對來說是比較簡單的, 直接使用嵌套就可以了.

    // 開啟子線程下載圖片
    // dispatch_sync 和 dispatch_async 兩者效果一樣,因為是在子線程下載的
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 網(wǎng)絡圖片 url
        NSURL *url = [NSURL URLWithString:@"http://pic12.nipic.com/20110114/6621051_221433460330_2.jpg"];
        
        // 下載二進制數(shù)據(jù)到本地
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 獲取圖片
        UIImage *image = [[UIImage alloc] initWithData:data];
        
        // 回到主線程刷新 UI 圖片
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
        
    });

這樣就能實現(xiàn)在子線程下載圖片,回到主線程刷新 UI 并設置圖片

GCD 常用函數(shù)

delay 延遲操作

先看前兩種方法

NSLog(@"-----start-----");
    
    // 延遲方法 第一種
    [self performSelector:@selector(task) withObject:nil afterDelay:3.0];
    
    // 第二種
    //[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(task) userInfo:nil repeats:YES];

方法實現(xiàn):

- (void)task {
    NSLog(@"task-%@", [NSThread currentThread]);
}

打印結果是一樣的

2016-07-28 13:27:16.779 GCD 常用函數(shù)[25917:1453136] -----start-----
2016-07-28 13:27:19.782 GCD 常用函數(shù)[25917:1453136] task-<NSThread: 0x7fa1ca604cf0>{number = 1, name = main}

只不過 NSTimer 會循環(huán)打印

用 GCD 會更簡單一些

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"GCD-%@", [NSThread currentThread]);
    });

不需要額外寫其他方法, 在 block 里直接聲明要執(zhí)行的任務就可以了.

2016-07-28 13:30:20.043 GCD 常用函數(shù)[26131:1456661] -----start-----
2016-07-28 13:30:23.342 GCD 常用函數(shù)[26131:1456661] GCD-<NSThread: 0x7fe4687013f0>{number = 1, name = main}

也能達到延遲操作的作用, 此時是默認在主線程中執(zhí)行的 . GCD 可以修改任務任務執(zhí)行所在的線程.

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"GCD-%@", [NSThread currentThread]);
    });

此時的執(zhí)行效果

2016-07-28 13:33:57.186 GCD 常用函數(shù)[26332:1459149] -----start-----
2016-07-28 13:34:00.188 GCD 常用函數(shù)[26332:1459321] GCD-<NSThread: 0x7f90c37146b0>{number = 2, name = (null)}

可以看到任務是在子線程中執(zhí)行的.

once 一次性執(zhí)行

直接上代碼

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"once - %@", [NSThread currentThread]);
    });

打印結果:

2016-07-28 13:37:36.106 GCD 常用函數(shù)[26532:1461714] once - <NSThread: 0x7fead2e01670>{number = 1, name = main}

之后就不會再運行了, 它是在整個運行程序中只會執(zhí)行一次, GCD 的一次性執(zhí)行代碼一般都是用在單例設計模式中.保證全局只有一個對象實例.

GCD 柵欄函數(shù)

在異步函數(shù)中控制任務執(zhí)行的順序, 只有當柵欄函數(shù)執(zhí)行完畢之后才會執(zhí)行后面的任務.

  • 注意:柵欄函數(shù)不能使用全局并發(fā)隊列

依然不善表達,直接上代碼, 為了能看出效果, 讓每個任務都執(zhí)行10次:

    // 創(chuàng)建并發(fā)隊列
    dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
    
    // 異步函數(shù)
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; ++i) {
            NSLog(@"download1 - %@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; ++i) {
            NSLog(@"download2 - %@", [NSThread currentThread]);
        }
    });
    
    // 柵欄函數(shù)
    dispatch_barrier_async(queue, ^{
        NSLog(@"++++++++++++++++++++++++++++++++++++++++");
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; ++i) {
            NSLog(@"download3 - %@", [NSThread currentThread]);
        }
    });
    

執(zhí)行結果:


只有在柵欄函數(shù)前面的任務全部執(zhí)行完畢后, 才會執(zhí)行后面的任務.

GCD 的 apply (快速迭代)

迭代: 也就是 遍歷
之前我們用的最多的就是 for 循環(huán)區(qū)遍歷10000次任務, 接下來我們就對比一下兩者有什么區(qū)別, 為了看出效果, 都加上耗時計算
首先是 for 循環(huán)

    NSDate* tmpStartData = [NSDate date];
    
    for (int i = 0; i < 10000; ++i) {
        NSLog(@"for- %d -- %@", i, [NSThread currentThread]);
    }
    
    double deltaTime = [[NSDate date] timeIntervalSinceDate:tmpStartData];
    NSLog(@"for 耗時 = %f", deltaTime);

運行結果:


for 循環(huán)用時約 10.5秒, 而且全部是在主線程中執(zhí)行的

接著用 GCD 的快速迭代

    NSDate* tmpStartData = [NSDate date];
    
    /*
     第一個參數(shù): 迭代次數(shù)
     第二個參數(shù): 線程隊列(并發(fā)隊列)
     第三個參數(shù): index 索引
     */
    dispatch_apply(10000, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSLog(@"GCD- %zd -- %@", index, [NSThread currentThread]);
    });
    
    double deltaTime = [[NSDate date] timeIntervalSinceDate:tmpStartData];
    NSLog(@"GCD 耗時 = %f", deltaTime);

運行結果



從上面兩張圖可以看出, GCD 快速迭代的是開啟了子線程去執(zhí)行的,而且主線程也參與了, 由于不是一個線程, 所以迭代也不是按順序的. 最后,用時5.08秒, 明顯快于 for 循環(huán)遍歷.

GCD 隊列組

隊列組的作用: 當執(zhí)行隊列組通知模塊時能保證放進隊列組里的任務全部執(zhí)行完畢了(之前那篇iOS - GCD 編程里也有類似介紹,不過那個是對 GCD 封裝過的方法)

```
// 創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();

//隊列組異步函數(shù)執(zhí)行任務
dispatch_group_async(group, queue, ^{
    NSLog(@"任務1 -- %@", [NSThread currentThread]);
});

dispatch_group_async(group, queue, ^{
    NSLog(@"任務2 -- %@", [NSThread currentThread]);
});

dispatch_group_async(group, queue, ^{
    NSLog(@"任務3 -- %@", [NSThread currentThread]);
});

// 隊列組攔截通知模塊(內(nèi)部本身是異步執(zhí)行的,不會阻塞線程)
dispatch_group_notify(group, queue, ^{
    NSLog(@"------隊列租任務執(zhí)行完畢-------");
});
```

執(zhí)行效果:


  • 關于 GCD 的相關知識點基本總結完畢, 下篇文章接著總結 NSOperation 的相關知識點

相關文章:
iOS 多線程知識點總結之: 進程和線程
iOS 多線程實現(xiàn)方案之 -- NSThread
iOS - GCD 編程

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绞绒,更是在濱河造成了極大的恐慌敌买,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纵柿,死亡現(xiàn)場離奇詭異却汉,居然都是意外死亡廊酣,警方通過查閱死者的電腦和手機锉罐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門帆竹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人脓规,你說我怎么就攤上這事栽连。” “怎么了侨舆?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵秒紧,是天一觀的道長。 經(jīng)常有香客問我挨下,道長熔恢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任臭笆,我火速辦了婚禮叙淌,結果婚禮上,老公的妹妹穿的比我還像新娘愁铺。我一直安慰自己鹰霍,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布茵乱。 她就那樣靜靜地躺著衅谷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪似将。 梳的紋絲不亂的頭發(fā)上获黔,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音在验,去河邊找鬼玷氏。 笑死,一個胖子當著我的面吹牛腋舌,可吹牛的內(nèi)容都是我干的盏触。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼块饺,長吁一口氣:“原來是場噩夢啊……” “哼赞辩!你這毒婦竟也來了?” 一聲冷哼從身側響起授艰,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤辨嗽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后淮腾,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糟需,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡屉佳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了洲押。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片武花。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖杈帐,靈堂內(nèi)的尸體忽然破棺而出体箕,到底是詐尸還是另有隱情,我是刑警寧澤挑童,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布干旁,位于F島的核電站,受9級特大地震影響炮沐,放射性物質(zhì)發(fā)生泄漏争群。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一大年、第九天 我趴在偏房一處隱蔽的房頂上張望换薄。 院中可真熱鬧,春花似錦翔试、人聲如沸轻要。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冲泥。三九已至,卻和暖如春壁涎,著一層夾襖步出監(jiān)牢的瞬間凡恍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工怔球, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嚼酝,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓竟坛,卻偏偏與公主長得像闽巩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子担汤,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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