iOS GCD的基本用法

1、GCD简介

Grand Central Dispatch (GCD),是苹果推出的多线程解决方案,它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。

2、任务和队列

  • 任务:
    就是执行操作的意思,在GCD中使用 block 封装。block就是一个提前准备好的代码块,在需要的时候执行
  • 队列:
    • 串行队列:一个接一个的调度任务
    • 并发队列:可以同时调度多个任务
    • 主队列: 专门用来在主线程上调度任务的"队列"
      主队列不能在其他线程中调度任务!
      如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!
      注意:主队列不是主线程
      • 异步任务,会在主线程的方法执行完成后,被调度
      • 同步任务,会造成死锁
    • 全局队列,系统提供给程序员,方便程序员使用的全局队列
      有关服务质量问题,使用以下代码能够做到 iOS7 & iOS8 的适配
      dispatch_get_global_queue(0, 0);
  • 执行任务的函数
    • 同步执行:当前指令不完成,就不会执行下一条指令
    • 异步执行:当前指令不完成,同样可以执行下一条指令
      异步是多线程的代名词
  • 小结:
    • 开不开线程,取决于执行任务的函数,同步不开,异步开
    • 开几条线程,取决于队列,串行开一条,并发开多条(异步)

3、GCD的基本使用

GCD的基本使用步骤有两步:

1、创建一个队列。
2、将任务添加到队列中,然后系统就会根据任务类型执行任务。(同步执行或异步执行)

串形队列+同步执行

不会开线程,会顺序执行

    // 1. 队列
    /**
     参数
     1. 队列的名称
     2. 队列的属性 
        DISPATCH_QUEUE_SERIAL(NULL) 表示串行
     */
    dispatch_queue_t q = dispatch_queue_create("com.chenhuan.queue", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"start");
    // 2. 执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");

输出结果

2017-07-29 19:08:27.030 Demo-GCD[942:62206] start
2017-07-29 19:08:27.030 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 0
2017-07-29 19:08:27.031 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 1
2017-07-29 19:08:27.031 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 2
2017-07-29 19:08:27.031 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 3
2017-07-29 19:08:27.031 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 4
2017-07-29 19:08:27.031 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 5
2017-07-29 19:08:27.032 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 6
2017-07-29 19:08:27.032 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 7
2017-07-29 19:08:27.032 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 8
2017-07-29 19:08:27.032 Demo-GCD[942:62206] <NSThread: 0x61800007eac0>{number = 1, name = main} 9
2017-07-29 19:08:27.032 Demo-GCD[942:62206] end
  • 串行队列 + 同步执行可以看到,所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行。
  • 所有任务都在打印的startend之间,这说明任务是添加到队列中马上执行的。

串形队列+异步执行

会开启新的线程,一个一个顺序执行

    dispatch_queue_t q = dispatch_queue_create("com.chenhuan.queue", NULL);
    NSLog(@"start");
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");

输出结果

2017-07-29 19:40:35.466 08-GCD演练[988:72561] start
2017-07-29 19:40:35.466 08-GCD演练[988:72561] end
2017-07-29 19:40:35.466 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 0
2017-07-29 19:40:35.467 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 1
2017-07-29 19:40:35.467 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 2
2017-07-29 19:40:35.467 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 3
2017-07-29 19:40:35.467 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 4
2017-07-29 19:40:35.467 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 5
2017-07-29 19:40:35.468 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 6
2017-07-29 19:40:35.468 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 7
2017-07-29 19:40:35.468 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 8
2017-07-29 19:40:35.468 08-GCD演练[988:72613] <NSThread: 0x610000072b00>{number = 3, name = (null)} 9
  • 串形队列+异步执行可以看到,会开启一条新的线程,并且一个一个执行
  • 所有任务是在打印的startend之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

并发队列+同步执行

不会开启线程,并且顺序执行

    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("com.chenhuan.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start");
    // 2. 异步执行
    for (int i = 0; i<10; i++) {
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");

输出结果

2017-07-29 20:38:10.211 Demo-GCD[1125:109556] start
2017-07-29 20:38:10.212 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 0
2017-07-29 20:38:10.212 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 1
2017-07-29 20:38:10.212 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 2
2017-07-29 20:38:10.212 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 3
2017-07-29 20:38:10.213 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 4
2017-07-29 20:38:10.213 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 5
2017-07-29 20:38:10.213 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 6
2017-07-29 20:38:10.213 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 7
2017-07-29 20:38:10.213 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 8
2017-07-29 20:38:10.214 Demo-GCD[1125:109556] <NSThread: 0x60800007a9c0>{number = 1, name = main} 9
2017-07-29 20:38:10.214 Demo-GCD[1125:109556] end
  • 并发队列 + 同步执行可以看到,所有任务都是在主线程中执行的,并没有开启新的线程。而且都是按顺序一个一个执行。
  • 所有任务都在打印的startend之间,这说明任务是添加到队列中马上执行的。

并发队列+异步执行

会开启多条线程,并且交替执行

    // 1. 队列
    dispatch_queue_t q = dispatch_queue_create("com.chenhuan.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"start");
    // 2. 异步执行
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"end");

输出结果

2017-07-29 20:36:52.573 Demo-GCD[1100:104147] start
2017-07-29 20:36:52.573 Demo-GCD[1100:104147] end
2017-07-29 20:36:52.573 Demo-GCD[1100:107782] <NSThread: 0x610000264e00>{number = 20, name = (null)} 0
2017-07-29 20:36:52.573 Demo-GCD[1100:108095] <NSThread: 0x618000262b40>{number = 39, name = (null)} 1
2017-07-29 20:36:52.574 Demo-GCD[1100:108096] <NSThread: 0x610000264f40>{number = 40, name = (null)} 2
2017-07-29 20:36:52.574 Demo-GCD[1100:108097] <NSThread: 0x60800026a200>{number = 41, name = (null)} 3
2017-07-29 20:36:52.574 Demo-GCD[1100:108099] <NSThread: 0x60800026a300>{number = 42, name = (null)} 4
2017-07-29 20:36:52.574 Demo-GCD[1100:108102] <NSThread: 0x610000266c40>{number = 43, name = (null)} 5
2017-07-29 20:36:52.574 Demo-GCD[1100:108100] <NSThread: 0x600000264d80>{number = 45, name = (null)} 7
2017-07-29 20:36:52.574 Demo-GCD[1100:108104] <NSThread: 0x600000264c00>{number = 44, name = (null)} 6
2017-07-29 20:36:52.574 Demo-GCD[1100:108106] <NSThread: 0x60800026a280>{number = 46, name = (null)} 8
2017-07-29 20:36:52.574 Demo-GCD[1100:108107] <NSThread: 0x60800026a2c0>{number = 47, name = (null)} 9

  • 并发队列 + 异步执行可以看到,所有任务都是在异步线程中执行。并且任务是交替着同时执行的。
  • 所有任务都在打印的startend之间,这说明任务是添加到队列中马上执行的。

主队列+同步执行

不要这样干,会造成死锁

    dispatch_queue_t q = dispatch_get_main_queue();
    
    NSLog(@"卡死了吗?");
    
    dispatch_sync(q, ^{
        NSLog(@"我来了");
    });
    
    NSLog(@"come here");

输出结果

2017-07-29 21:13:23.550 Demo-GCD[1148:120053] 卡死了吗?
  • 如果你自己运行以上代码,会发现程序不能运行了。这是因为照成了死锁
  • 我们知道dispatch_sync表示同步的执行任务,也就是说执行dispatch_sync后,当前队列会阻塞。而dispatch_sync中的block如果要在当前队列中执行,就得等待当前队列程执行完成。
  • 在上面这个例子中,主队列在执行dispatch_sync,随后队列中新增一个任务block。因为主队列是同步队列,所以block要等dispatch_sync执行完才能执行,但是dispatch_sync是同步派发,要等block执行完才算是结束。在主队列中的两个任务互相等待,导致了死锁。

主队列+异步执行

    // 1. 主队列 - 程序启动之后已经存在主线程,主队列同样存在
    dispatch_queue_t q = dispatch_get_main_queue();
    
    // 2. 安排一个任务
    for (int i = 0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    NSLog(@"睡会");
    [NSThread sleepForTimeInterval:2.0];
    NSLog(@"come here");

输出结果

2017-07-29 21:21:30.547 Demo-GCD[1208:127513] 睡会
2017-07-29 21:21:32.548 Demo-GCD[1208:127513] come here
2017-07-29 21:21:32.548 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 0
2017-07-29 21:21:32.548 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 1
2017-07-29 21:21:32.549 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 2
2017-07-29 21:21:32.549 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 3
2017-07-29 21:21:32.549 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 4
2017-07-29 21:21:32.550 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 5
2017-07-29 21:21:32.550 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 6
2017-07-29 21:21:32.550 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 7
2017-07-29 21:21:32.550 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 8
2017-07-29 21:21:32.551 Demo-GCD[1208:127513] <NSThread: 0x600000076d40>{number = 1, name = main} 9
  • 不开线程,顺序执行
  • 从上面可以看出,异步执行不会阻塞线程

全局队列

    /**
     参数
     1. 涉及到系统适配
     iOS 8       服务质量(让线程响应的更快还是更慢)
     - QOS_CLASS_USER_INTERACTIVE           用户交互(用户迫切希望线程快点被执行,不要用耗时的操作)
     - QOS_CLASS_USER_INITIATED             用户需要的(同样不要使用耗时操作)
     - QOS_CLASS_DEFAULT                    默认的(给系统用来重置队列的)
     ** QOS_CLASS_UTILITY                    实用工具(用来做耗时操作)
     - QOS_CLASS_BACKGROUND                 后台
     - QOS_CLASS_UNSPECIFIED                没有指定优先级
     
     iOS 7       调度的优先级
     - DISPATCH_QUEUE_PRIORITY_HIGH 2       高优先级
     - DISPATCH_QUEUE_PRIORITY_DEFAULT 0    默认优先级
     - DISPATCH_QUEUE_PRIORITY_LOW (-2)     低优先级
     - DISPATCH_QUEUE_PRIORITY_BACKGROUND   后台优先级
     
     提示:尤其不要选择 BACKGROUND 优先级和服务质量,用户不需要知道线程什么时候执行完成!线程的执行会慢的令人发指!
     
     有关服务质量的介绍,用在与 XPC 框架结合使用的,XPC 用在 MAC 平台上做进程间通讯的框架!
     
     因为大家工作后,暂时会考虑 iOS7 & iOS8 的适配,无法使用服务质量,直接指定 0,能够做到 iOS7 & 8 的适配
     dispatch_get_global_queue(0, 0);
     
     2. 为未来使用保留的,应该始终传入0
     */
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i < 10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    NSLog(@"com here");

  • 全局队列可以当作并发队列来使用。一般我们在开发过程中都是使用global_queue

4、GCD的其他一些用法

dispatch_group

在实际开发中,有的时候,会需要同时监听多个异步任务最终完成的情况!这个时候就需要用到我们的 dispatch_group

  // 1. 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 2. 调度组
    dispatch_group_t group = dispatch_group_create();
    
    // 3. 添加任务,让队列调度,指定任务执行函数,最终通知群组
    dispatch_group_async(group, queue, ^{
        NSLog(@"download A %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:1.0];
        NSLog(@"download B %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:0.8];
        NSLog(@"download C %@", [NSThread currentThread]);
    });
    
    // 4. 所有任务执行完毕后,获得通知
    // 用一个调度组,可以监听全局队列调度的任务,执行完毕后,在主队列执行最终处理!
    // dispatch_group_notify 本身是异步的
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 更新UI,通知用户!
        NSLog(@"OK %@", [NSThread currentThread]);
    });
    
    NSLog(@"come here");

输出结果

2017-07-29 21:31:18.362 Demo-GCD[1255:135674] come here
2017-07-29 21:31:18.362 Demo-GCD[1255:135726] download A <NSThread: 0x60000006e040>{number = 3, name = (null)}
2017-07-29 21:31:19.166 Demo-GCD[1255:135723] download C <NSThread: 0x60000006b200>{number = 4, name = (null)}
2017-07-29 21:31:19.362 Demo-GCD[1255:135741] download B <NSThread: 0x60000006fd80>{number = 5, name = (null)}
2017-07-29 21:31:19.363 Demo-GCD[1255:135674] OK <NSThread: 0x60800006c540>{number = 1, name = main}

首先我们要通过 dispatch_group_create() 方法生成一个组。

接下来,我们把 dispatch_async 方法换成 dispatch_group_async。这个方法多了一个参数,第一个参数填刚刚创建的分组。

最后调用 dispatch_group_notify 方法。这个方法表示把第三个参数 block 传入第二个参数队列中去。而且可以保证第三个参数 block 执行时,group 中的所有任务已经全部完成。

dispatch_group 第二种调用方式

    // 1. 队列
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    
    // 2. 调度组
    dispatch_group_t g = dispatch_group_create();
    
    // 3. 进入群组,执行此函数后,再添加的异步执行的block,会被group监听
    // dispatch_group_enter & dispatch_group_leave一定要配对出现
    dispatch_group_enter(g);
    
    // 4. 添加任务
    dispatch_async(q, ^{
        [NSThread sleepForTimeInterval:10.0];
        NSLog(@"download A");
        // 异步任务中,所有的代码执行完毕后,最后离开群组
        dispatch_group_leave(g);
    });
    
    // 再次添加任务
    dispatch_group_enter(g);
    
    // 5. 添加任务 B
    dispatch_async(q, ^{
        NSLog(@"download B");
        // 异步任务中,所有的代码执行完毕后,最后离开群组
        dispatch_group_leave(g);
    });
    
//     6. 拦截通知
//        dispatch_group_notify(g, q, ^{
//            NSLog(@"Over");
//        });
    // 等待到永远,死等,阻塞住线程执行,一直到所有的任务执行完毕,才会执行后续的代码!
    long time = dispatch_group_wait(g, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
    
    NSLog(@"result=%ld",time);
    NSLog(@"come here");

输出结果

2017-07-29 21:52:08.123 Demo-GCD[1326:154377] download B
2017-07-29 21:52:10.124 Demo-GCD[1326:154323] result=49
2017-07-29 21:52:10.124 Demo-GCD[1326:154323] come here
2017-07-29 21:52:18.126 Demo-GCD[1326:154391] download A

dispatch_group_wait 方法是一个很有用的方法,它的完整定义如下:

long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

第一个参数表示要等待的 group,第二个则表示等待时间。返回值表示经过指定的等待时间,属于这个 group 的任务是否已经全部执行完,如果是则返回 0,否则返回非 0。

第二个 dispatch_time_t 类型的参数还有两个特殊值:DISPATCH_TIME_NOWDISPATCH_TIME_FOREVER

前者表示立刻检查属于这个 group 的任务是否已经完成,后者则表示一直等到属于这个 group 的任务全部完成。

dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC))传入这个参数表示等待两秒后检查执行情况。显然没有执行完成 result=49

dispatch_after

通过 GCD 还可以进行简单的定时操作,比如在 3 秒后执行某个 block 。代码如下:

    NSLog(@"come here");
    
    /**
     参数:
     从现在开始,经过多少纳秒之后,让 queue 队列,调度 block 任务,异步执行!
     
     1. when
     2. queue
     3. block
    // 从现在开始,经过多少纳秒之后
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
    
    // 主队列
//    dispatch_after(when, dispatch_get_main_queue(), ^{
//        NSLog(@"%@", [NSThread currentThread]);
//    });
    // 全局队列
//    dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
//        NSLog(@"%@", [NSThread currentThread]);
//    });
    // 串行队列
    dispatch_after(when, dispatch_queue_create("com.chenhuan.queue", NULL), ^{
        NSLog(@"%@", [NSThread currentThread]);
    });

dispatch_after 方法有三个参数。第一个表示时间,也就是从现在起往后三秒钟。第二、三个参数分别表示要提交的任务和提交到哪个队列。

dispatch_once

dispathc_once 函数可以确保某个 block 在应用程序执行的过程中只被处理一次,而且它是线程安全的。所以单例模式可以很简单的实现,以 OC 中单例类为例

+ (instancetype)sharedInstance {
    static id instance = nil;
    static dispatch_once_t once;
    dispatch_once($once, ^{
        instance = [[self alloc] init];
    });

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

推荐阅读更多精彩内容