3、GCD-Objc - Part 1

GCD 两个核心概念:

  • 队列: 用来存放任务
  • 任务: 执行什么操作, 任务是使用 block 封装的代码块.

GCD会自动将队列中的任务取出,放到对应的线程中执行.
任务的取出遵循队列的FIFO原则:先进先出,后进后出

GCD 有两个执行任务的函数:


// 同步执行
void
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

// 异步执行
void
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

两个函数只有两个参数:
1. dispatch_queue_t queue  : 调度的队列
2. dispatch_block_t block : 调度的任务

GCD 使用的基本思路是: 
1. 创建队列
2. 创建任务
3. 将任务添加到队列

这两个函数和上面是对应的。 上面的函数是下面函数的封装。实际使用的还是下面的函数。由于使用起来比较麻烦,基本不怎么使用。

void
dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);

void
dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);

/*
  参数:
    - dispatch_queue_t queue:       要添加函数的队列
    -  void *context:               传递到函数中的参数
    - dispatch_function_t work    : 队列中药执行的函数
*/ 

typedef void (^dispatch_block_t) (void);         // 匿名函数(无参数无返回值)
typedef void (*dispatch_function_t) (void *);    // 函数的指针

队列的类型
(可以从函数的类型中看出)

  • 并发队列(Concurrent Dispatch Queue): 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务), 并发功能只有在异步(dispatch_async)函数下才有效

  • 串行队列(Serial Dispatch Queue): 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务

队列中任务的执行方式
同步和异步的区别:

  • 同步:在当前线程中执行,当前代码不执行完,就不能够执行下一条代码。会阻塞当前线程。

  • 异步:在另一条线程中执行(不用等待当前代码执行完,就能够执行下一条),不会阻塞当前线程。

1、常见的队列:

主队列
默认是串行队列,队列中的任务一次只能执行一个。
(主队列中只有一个线程,主线程)
可以

// 队列的获取
dispatch_queue_t q = dispatch_get_main_queue();


// 函数的声明
dispatch_queue_t
dispatch_get_main_queue(void)
{
    return DISPATCH_GLOBAL_OBJECT(dispatch_queue_t, _dispatch_main_q);
}

全局队列:
默认是并发队列。
全局队列是为了方便 GCD 使用,苹果默认提供的。我们值需要获取。
全局本身就是并发的。
全局队列 apple 也会使用。更具服务质量,我们有四个类型的全局队列可以使用。

// 队列的获取
// 参数一般默认填 0, 0
dispatch_queue_t q = dispatch_get_global_queue(0, 0);



// 函数的声明
/*
全局队列函数参数说明:
    * 第一个参数: dispatch_queue_priority_tpriority  队列的优先级
       *  - DISPATCH_QUEUE_PRIORITY_HIGH:         QOS_CLASS_USER_INITIATED   // 高优先级, 默认值为 2
       *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT          // 默认优先级, 默认值为 0
       *  - DISPATCH_QUEUE_PRIORITY_LOW:          QOS_CLASS_UTILITY          // 低优先级, 默认值为 -2
       *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND       // 后台优先级

    *  unsigned longflags : 队列的标记, 默认填 0 ,标记的结果就会返回 null 值
       flags : 标记, 一般都是为了后期使用, 主要是用来区分队列.

    (为了使线程操作不那么的复杂,我们一般使用默认的优先级. 看到这里就知道为什么要填写 0, 0 吧)


关于服务质量的说明:
1. iOS 8.0 后
    告诉队列执行任务的"服务质量quality of service"
    QOS_CLASS_USER_INTERACTIVE 0x21,              用户交互(希望尽快完成,用户对结果很期望,不要放太耗时操作)       
    QOS_CLASS_USER_INITIATED 0x19,                用户期望(不要放太耗时操作)
    QOS_CLASS_DEFAULT 0x15,                       默认(不是给程序员使用的,用来重置对列使用的)
    QOS_CLASS_UTILITY 0x11,                       实用工具(耗时操作,可以使用这个选项)
    QOS_CLASS_BACKGROUND 0x09,                    后台
    QOS_CLASS_UNSPECIFIED 0x00,                   未指定(现在好像已经移除)

2. iOS 7.0 之前 优先级
    DISPATCH_QUEUE_PRIORITY_HIGH 2                高优先级
    DISPATCH_QUEUE_PRIORITY_DEFAULT 0             默认优先级
    DISPATCH_QUEUE_PRIORITY_LOW (-2)              低优先级
    DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN  后台优先级

        提示:不要选择 BACKGROUND 的选项,苹果认为:BACKGROUND 表示用户不需要知道任务什么时候完成!
                   选择这个选项,速度慢的令人发指!不利于调试!
                  关于优先级,不要搞太负责,就用最简单的

       结论:如果要做 iOS 8.0 & iOS 7.0 的适配: dispatch_get_global_queue(0, 0);
         
      提示:如果在今后,iOS 8.0 & 9.0 适配的时候,应该选择 QOS_CLASS_UTILITY
    
      此内容只作为参考,具体查看 api 文档说明!
*/ 
dispatch_queue_t
dispatch_get_global_queue(long identifier, unsigned long flags);

自己创建队列:

// 创建一个队列
// 此队列为串行队列
 dispatch_queue_t q = dispatch_queue_create("demo", NULL);


// 函数的声明
/*
 参数说明:
            * const char *label: 队列的名称
            * dispatch_queue_attr_t attr  : 队列的类型
                - DISPATCH_QUEUE_SERIAL(NULL)   串行队列
                - DISPATCH_QUEUE_CONCURRENT     并发队列
*/
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

队列的区分:

  • 主队列:在 app 启动的时候就创建的。我们只需要获取。默认情况下主队列只有1条线程,叫主线程。
  • 全局队列:apple 提供的一个并发队列。根据不同的优先级(服务质量)可以获取 5 种不同的全局队列
  • 自己创建的队列:这个队列是自己通过函数创建的。
    (我们可以操作的队列大约有 6 个。)

2、队列选择的问题:

我们使用 GCD 的目的只有一个:开辟新的线程执行耗时操作。
一般情况下我不需要创建队列,只需要使用apple 为我们给提供的全局队列。进行异步执行任务就可以了。
只有在第三方框架或者大型商业软件才使用自己创建的队列执行异步操作。

  • 并发队列 (全局队列)

    • 调度任务的时候,多个线程,而且是顺序不固定
    • 并发能力好
    • 效率高,速度快,费电,费钱
  • 串行队列

    • 调度任务,只有一条线程,顺序执行每一个任务
    • 并发能力差
    • 效率差,速度慢,省电,省钱
    • 用户并不是什么时候都会希望快的!

选择依据:

  • 如果是WIFI 情况,使用全局队列,开启线程的数量6 条左右
  • 如果是3G/4G,使用串行队列,如果使用全局队列,开启线程的数量控制在2~3 条

1、全局队列异步执行

(全局队列默认是一个并发队列)
异步会开线程
objc 版本

// 1、获取全局队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
NSLog(@"before: %@", [NSThread currentThread]);
// 2、创建任务
void(^task)() = ^() {
    NSLog(@"task : %@", [NSThread currentThread]);  
};
// 3、将任务添加进异步队列
dispatch_async(q, task);

NSLog(@"after: %@", [NSThread currentThread]);

// 打印结果
2016-06-21 10:56:37.681 Thread-Objc[43762:2374000] before: <NSThread: 0x7fa5ebf02d10>{number = 1, name = main}
2016-06-21 10:56:37.681 Thread-Objc[43762:2374000] after: <NSThread: 0x7fa5ebf02d10>{number = 1, name = main}
2016-06-21 10:56:37.681 Thread-Objc[43762:2374217] task : <NSThread: 0x7fa5ebe01410>{number = 2, name = (null)}

2、全局队列同步执行

(全局队列默认是一个并发队列)
同步执行不会开线程
objc 版本

// 1、获取全局队列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);    

NSLog(@"before: %@", [NSThread currentThread]);
// 2、创建任务
void(^task)() = ^() {
    NSLog(@"task : %@", [NSThread currentThread]);
};    
// 3、将任务添加进异步队列
 dispatch_sync(q, task);

NSLog(@"after: %@", [NSThread currentThread]);

// 打印结果
2016-06-21 10:53:47.219 Thread-Objc[43695:2369064] before: <NSThread: 0x7fcdb1f04420>{number = 1, name = main}
2016-06-21 10:53:47.220 Thread-Objc[43695:2369064] task : <NSThread: 0x7fcdb1f04420>{number = 1, name = main}
2016-06-21 10:53:47.220 Thread-Objc[43695:2369064] after: <NSThread: 0x7fcdb1f04420>{number = 1, name = main}

结论:

  • 同步:不会开启线程,就在当前线程执行
  • 异步:会开启线程,在其他线程执行block
    (不适用于主队列)

多线程体验代码

for (int index = 0; index < 10; index ++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
}

// 打印结果
2016-06-21 11:03:57.780 Thread-Objc[43896:2382918] task : <NSThread: 0x7ff9b0c06070>{number = 5, name = (null)}
2016-06-21 11:03:57.780 Thread-Objc[43896:2382919] task : <NSThread: 0x7ff9b0f09e00>{number = 3, name = (null)}
2016-06-21 11:03:57.780 Thread-Objc[43896:2382911] task : <NSThread: 0x7ff9b0c0b920>{number = 2, name = (null)}
2016-06-21 11:03:57.780 Thread-Objc[43896:2382929] task : <NSThread: 0x7ff9b0e00660>{number = 4, name = (null)}
2016-06-21 11:03:57.781 Thread-Objc[43896:2382930] task : <NSThread: 0x7ff9b0d96680>{number = 6, name = (null)}
2016-06-21 11:03:57.781 Thread-Objc[43896:2382918] task : <NSThread: 0x7ff9b0c06070>{number = 5, name = (null)}
2016-06-21 11:03:57.781 Thread-Objc[43896:2382919] task : <NSThread: 0x7ff9b0f09e00>{number = 3, name = (null)}
2016-06-21 11:03:57.781 Thread-Objc[43896:2382911] task : <NSThread: 0x7ff9b0c0b920>{number = 2, name = (null)}
2016-06-21 11:03:57.781 Thread-Objc[43896:2382929] task : <NSThread: 0x7ff9b0e00660>{number = 4, name = (null)}
2016-06-21 11:03:57.781 Thread-Objc[43896:2382930] task : <NSThread: 0x7ff9b0d96680>{number = 6, name = (null)}

// 能够体现出gcd 底层是有线程池的 (number 体现出线程出现了复用)
// 一个任务执行完毕后,线程可以被复用!
// 如果用 NSThread,需要自己维护线程池,以及管理所有线程的生命周期!
// 在实际开发中,新建线程是一个非常频繁的工作!(GCD 不需要程序员管理线程的创建工作)

3、GCD 中多线程最简单的应用代码 ( 线程间通讯)

//  全局队列 - 负责调度任务的
//  异步执行任务
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    // 耗时操作的任务
    NSLog(@"task : %@", [NSThread currentThread]);
    
    
    // 通知主线程刷新界面
    /*
        这里使用  dispatch_async()  和  dispatch_sync() 效果是不一样的。
        dispatch_sync() 会等待当前线程执行完毕后才会执行 block 中的内容
        dispatch_async()  不会等待,会先答应 over 后再执行 block 中的内容。
    */ 
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"UI - task : %@", [NSThread currentThread]);
    });
    
    NSLog(@"over : %@", [NSThread currentThread]);
});

队列同步异步,串行并行之间的关系

1、 队列和任务之间的关系(串行并行同步异步的组合)

  • 串行队列异步执行
    NSLog(@"before: %@", [NSThread currentThread]);
  // 创建一个队列(串行)
dispatch_queue_t q = dispatch_queue_create("demo1", NULL);

  // 5 次异步
for (int index = 0; index < 5; index ++) {
    
    // 将任务添加到队列
    dispatch_async(q, ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
  }

  NSLog(@"after: %@", [NSThread currentThread]);

  // 打印结果
  2016-06-21 11:33:10.502 Thread-Objc[44380:2420806] before: <NSThread: 0x7fdf0be02f80>{number = 1, name = main}
  2016-06-21 11:33:10.502 Thread-Objc[44380:2420806] after: <NSThread: 0x7fdf0be02f80>{number = 1, name = main}
  2016-06-21 11:33:10.502 Thread-Objc[44380:2420806] <OS_dispatch_queue: demo1[0x7fdf0bd0dbb0]>
2016-06-21 11:33:10.502 Thread-Objc[44380:2421052] task : <NSThread: 0x7fdf0be25210>{number = 2, name = (null)}
2016-06-21 11:33:10.503 Thread-Objc[44380:2421052] task : <NSThread: 0x7fdf0be25210>{number = 2, name = (null)}
2016-06-21 11:33:10.503 Thread-Objc[44380:2421052] task : <NSThread: 0x7fdf0be25210>{number = 2, name = (null)}
2016-06-21 11:33:10.503 Thread-Objc[44380:2421052] task : <NSThread: 0x7fdf0be25210>{number = 2, name = (null)}
2016-06-21 11:33:10.503 Thread-Objc[44380:2421052] task : <NSThread: 0x7fdf0be25210>{number = 2, name = (null)}

异步执行会开线程,串行开1 条, NSLog(@"after: %@", [NSThread currentThread])执行的位置是不确定的。(将异步次数你就可以看到效果)
  • 串行队列同步执行
NSLog(@"before: %@", [NSThread currentThread]);
// 创建一个队列(串行)
dispatch_queue_t q = dispatch_queue_create("demo1", NULL);

// 5 次异步
for (int index = 0; index < 5; index ++) {
    
    // 将任务添加到队列
    dispatch_sync(q, ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
}

NSLog(@"after: %@", [NSThread currentThread]);

// 打印结果
2016-06-21 11:36:04.743 Thread-Objc[44420:2424798] before: <NSThread: 0x7ffd70d06750>{number = 1, name = main}
2016-06-21 11:36:04.744 Thread-Objc[44420:2424798] task : <NSThread: 0x7ffd70d06750>{number = 1, name = main}
2016-06-21 11:36:04.744 Thread-Objc[44420:2424798] task : <NSThread: 0x7ffd70d06750>{number = 1, name = main}
2016-06-21 11:36:04.744 Thread-Objc[44420:2424798] task : <NSThread: 0x7ffd70d06750>{number = 1, name = main}
2016-06-21 11:36:04.744 Thread-Objc[44420:2424798] task : <NSThread: 0x7ffd70d06750>{number = 1, name = main}
2016-06-21 11:36:04.744 Thread-Objc[44420:2424798] task : <NSThread: 0x7ffd70d06750>{number = 1, name = main}
2016-06-21 11:36:04.744 Thread-Objc[44420:2424798] after: <NSThread: 0x7ffd70d06750>{number = 1, name = main}

同步执行不会开线程,只会在主线中运行,NSLog(@"after: %@", [NSThread currentThread])执行的位置是不确定的。(将异步次数你就可以看到效果)
  • 并行队列异步执行
NSLog(@"before: %@", [NSThread currentThread]);
// 创建一个队列(串行)
dispatch_queue_t q = dispatch_queue_create("demo1", DISPATCH_QUEUE_CONCURRENT);

// 5 次异步
for (int index = 0; index < 5; index ++) {
    
    // 将任务添加到队列
    dispatch_async(q, ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
}

NSLog(@"after: %@", [NSThread currentThread]);

// 打印结果
2016-06-21 11:49:06.187 Thread-Objc[44535:2441876] before: <NSThread: 0x7fd181400d20>{number = 1, name = main}
2016-06-21 11:49:06.189 Thread-Objc[44535:2441876] after: <NSThread: 0x7fd181400d20>{number = 1, name = main}
2016-06-21 11:49:06.190 Thread-Objc[44535:2442143] task : <NSThread: 0x7fd18147f7e0>{number = 2, name = (null)}
2016-06-21 11:49:06.191 Thread-Objc[44535:2442152] task : <NSThread: 0x7fd181521180>{number = 3, name = (null)}
2016-06-21 11:49:06.191 Thread-Objc[44535:2442159] task : <NSThread: 0x7fd18164a590>{number = 4, name = (null)}
2016-06-21 11:49:06.191 Thread-Objc[44535:2442171] task : <NSThread: 0x7fd181415cf0>{number = 5, name = (null)}
2016-06-21 11:49:06.191 Thread-Objc[44535:2442143] task : <NSThread: 0x7fd18147f7e0>{number = 2, name = (null)}

异步执行会开起线程,开启的线程数是不确定的,NSLog(@"after: %@", [NSThread currentThread])执行的位置是不确定的。(将异步次数你就可以看到效果)
  • 并行队列同步执行
NSLog(@"before: %@", [NSThread currentThread]);
// 创建一个队列(串行)
dispatch_queue_t q = dispatch_queue_create("demo1", DISPATCH_QUEUE_CONCURRENT);

// 5 次异步
for (int index = 0; index < 5; index ++) {
    
    // 将任务添加到队列
    dispatch_sync(q, ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
}

NSLog(@"after: %@", [NSThread currentThread]);

// 打印结果
2016-06-21 11:53:47.185 Thread-Objc[44570:2447335] before: <NSThread: 0x7fcde34084b0>{number = 1, name = main}
2016-06-21 11:53:47.187 Thread-Objc[44570:2447335] task : <NSThread: 0x7fcde34084b0>{number = 1, name = main}
2016-06-21 11:53:47.187 Thread-Objc[44570:2447335] task : <NSThread: 0x7fcde34084b0>{number = 1, name = main}
2016-06-21 11:53:47.187 Thread-Objc[44570:2447335] task : <NSThread: 0x7fcde34084b0>{number = 1, name = main}
2016-06-21 11:53:47.188 Thread-Objc[44570:2447335] task : <NSThread: 0x7fcde34084b0>{number = 1, name = main}
2016-06-21 11:53:47.188 Thread-Objc[44570:2447335] task : <NSThread: 0x7fcde34084b0>{number = 1, name = main}
2016-06-21 11:53:47.188 Thread-Objc[44570:2447335] after: <NSThread: 0x7fcde34084b0>{number = 1, name = main}

通过不会开线程,默认会在主线运行,NSLog(@"after: %@", [NSThread currentThread])执行的位置是不确定的。(将异步次数你就可以看到效果)

总结:
同步不开异步开,串行开一条,并发开多条。
(适用于自己创建的队列)

2、主队列同步和异步执行

主队列默认就是一个串行队列
主队列,专门负责在主线程上调度任务,所有的任务执行就应该在主线程上执行!
(主队列不具有开启线程的能力 无论异步还是同步)

  • 主队列异步
NSLog(@"before: %@", [NSThread currentThread]);
// 主队列 - 程序一启动,主线程就已经存在,主队列也同时 就存在了,只需要获取不需要创建
dispatch_queue_t q = dispatch_get_main_queue();

// 5 次异步
for (int index = 0; index < 5; index ++) {
    
    // 将任务添加到队列
    dispatch_async(q, ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
}

// 进行线程阻塞操作
[NSThread sleepForTimeInterval:1.0];
NSLog(@"after: %@", [NSThread currentThread]);

// 打印结果
2016-06-21 12:00:42.136 Thread-Objc[44680:2455130] before: <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}
2016-06-21 12:00:42.137 Thread-Objc[44680:2455130] after: <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}
2016-06-21 12:00:42.148 Thread-Objc[44680:2455130] task : <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}
2016-06-21 12:00:42.150 Thread-Objc[44680:2455130] task : <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}
2016-06-21 12:00:42.150 Thread-Objc[44680:2455130] task : <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}
2016-06-21 12:00:42.151 Thread-Objc[44680:2455130] task : <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}
2016-06-21 12:00:42.151 Thread-Objc[44680:2455130] task : <NSThread: 0x7fb4f1c053e0>{number = 1, name = main}

主队列异步是不会开启线程的,NSLog(@"after: %@", [NSThread currentThread])执行的位置是不确定的。(将异步次数你就可以看到效果)
  • 主队列同步 (会造成死锁)
NSLog(@"before: %@", [NSThread currentThread]);
// 创建一个队列(串行)
dispatch_queue_t q = dispatch_get_main_queue();

// 5 次异步
for (int index = 0; index < 5; index ++) {
    
    // 将任务添加到队列
    dispatch_sync(q, ^{
        NSLog(@"task : %@", [NSThread currentThread]);
    });
}

NSLog(@"after: %@", [NSThread currentThread]);

打印结果:
2016-06-21 12:07:22.921 Thread-Objc[44793:2467944] before: <NSThread: 0x7fb84a500ac0>{number = 1, name = main}

测试的结果是 : 死锁
关于死锁:记住主队列的特点就容易理解!主线程有任务就暂时不调度任务!

关于主队列不死锁的问题
( 就是我们常用的项目代码,只是将主线程异步改为同步)

使用的方法是 block 的嵌套调用
// 创建并发队列
dispatch_queue_t q = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);


// 使用 block 对任务进行嵌套
void(^task)() = ^() {
 
    NSLog(@"Befor: %@",[NSThread currentThread]);
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"Main Task: %@",[NSThread currentThread]);
    });
    
    NSLog(@"After: %@",[NSThread currentThread]);
};

// 将任务添加到异步队列
dispatch_async(q, task);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,183评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,850评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,766评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,854评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,871评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,457评论 1 311
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,999评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,914评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,465评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,543评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,675评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,354评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,029评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,514评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,616评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,091评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,685评论 2 360

推荐阅读更多精彩内容