iOS中GCD的深入浅出【图文并茂】

1.什么是GCD

全称是Grand Central Dispatch 可以理解为"牛逼的中枢调度器"
纯C语言,提供了非常多非常强大的函数GCD比之 NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分。

2.GCD的优点

易用性:GCD比NSthread简单易用,由于GCD是基于,work unit而并非像thread那样基于运算,导致GCD易用的原因有一部分在于你可以不用担心太多的效率问题而仅仅使用它就行了(就是告诉GCD我要执行什么任务,不需要编写任何线程管理代码)。

效率高:GCD比较轻量,使得在很多地方比单独创建消耗内存的线程实用而且速度快。

性能: GCD会自动根据系统负载来增减线程数量,这就减少了上下文切换以及增加了计算效率。

2.GCD2个核心概念

任务:执行什么操作
队列:用来存放任务

3.GCD的使用

GCD的使用步骤其实很简单,只有两步。

  1. 创建一个队列(并行或者串行)
  2. 将任务添加到队列中,系统会根据任务类型来执行任务(任务只有异步和同步)

创建队列

  • 可以创建dispatch_queue_create来创建对象,需要传入两个参数,第一个表示唯一表示符,用于DeBug,可为空;第二个参数用来识别是串行还是并行。DISPATCH_QUEUE_CONCURRENT表示并行,DISPATCH_QUEUE_SERIAL表示串行。
//并行队列方法
 dispatch_queue_t  queue=dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
 //串行队列方法
 dispatch_queue_t  queue=dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
  • 全局并发队
    本质是一个并发队列,由系统提供,方便编程,可以不用创建就直接使用
    获取全局队列的方法: dispatch_get_global_queue(long indentifier.unsigned long flags)
    参数说明:参数1:代表该任务的优先级,默认写0就行,不要使用系统提供的枚举类型,因为ios7和ios8的枚举数值不一样,使用数字可以通用。参数2:苹果保留关键字,一般也写0
//全局并发队列
dispatch_queue_t queue1=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 全局队列和并发队列的区别:
  1. 全局队列没有名字,但是并发队列有名字。有名字可以便于查看系统日志
  2. 全局队列是所有应用程序共享的。
  3. 在mrc的时候,全局队列不用手动释放,但是并发队列需要。

任务创建

  • 同步执行任务
dispatch_sync(queue, ^{
       NSLog(@"同步------%@",[NSThread currentThread]);
    });
  • 异步步执行任务
dispatch_async(queue, ^{
       NSLog(@"异步------%@",[NSThread currentThread]);
    });

队列+任务组合

虽然使用GCD只需两步,但是既然我们有两种队列,两种任务执行方式,那么我们就有了四种不同的组合方式。这四种不同的组合方式是

  1. 并行队列 + 同步执行
  2. 并行队列+异步执行
  3. 串行队列+同步执行
  4. 串行队列+异步执行

实际上,我们还有一种特殊队列是主队列,那样就有六种不同的组合方式了。

  1. 主队列 + 同步执行
  2. 主队列+异步执行

这些组合的方式,得到的结果是这样的如图:

并行队列 串行队列 主队列
同步执行 不开启新的线程,串行执行任务 不开启新的线程,串行执行任务 不开启新的线程,串行执行任务
异步执行 开启新的线程,并行执行任务 开启新的线程,并行执行任务 不开启新的线程,串行执行任务

4.GCD的基本使用

  • 并行队列 + 同步执行

不会开启新线程,执行完一个任务,再执行下一个任务

 NSLog(@"asyncConcurrent---begin");
    
    //创建并行队列方法
    dispatch_queue_t  queue=dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    //创建同步执行任务
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
   
    NSLog(@"asyncConcurrent---end");

执行结果

2017-08-09 18:17:43.054 GCDdemo[6942:296736] asyncConcurrent---begin
2017-08-09 18:17:43.054 GCDdemo[6942:296736] 任务1------<NSThread: 0x60800006ce80>{number = 1, name = main}
2017-08-09 18:17:43.055 GCDdemo[6942:296736] 任务1------<NSThread: 0x60800006ce80>{number = 1, name = main}
2017-08-09 18:17:43.055 GCDdemo[6942:296736] 任务2------<NSThread: 0x60800006ce80>{number = 1, name = main}
2017-08-09 18:17:43.055 GCDdemo[6942:296736] 任务2------<NSThread: 0x60800006ce80>{number = 1, name = main}
2017-08-09 18:17:43.055 GCDdemo[6942:296736] 任务3------<NSThread: 0x60800006ce80>{number = 1, name = main}
2017-08-09 18:17:43.056 GCDdemo[6942:296736] 任务3------<NSThread: 0x60800006ce80>{number = 1, name = main}
2017-08-09 18:17:43.056 GCDdemo[6942:296736] asyncConcurrent---end

  1. 并行队列 + 同步执行中可以看到,所有任务都是在主线程中执行的。由于只有一个线程,所以任务只能一个一个执行。
  2. 同时我们还可以看到,所有任务都在打印的syncConcurrent---beginsyncConcurrent---end之间,这说明任务是添加到队列中马上执行的
  • 并行队列 + 异步执行

开启新线程,任务交替执行

 NSLog(@"asyncConcurrent---begin");
    
    //创建并行队列方法
    dispatch_queue_t  queue=dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    
    //创建异步执行任务
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
   
    NSLog(@"asyncConcurrent---end");

执行结果

2017-08-09 19:03:00.924 GCDdemo[7093:310628] asyncConcurrent---begin
2017-08-09 19:03:00.925 GCDdemo[7093:310628] asyncConcurrent---end
2017-08-09 19:03:00.925 GCDdemo[7093:310764] 任务1------<NSThread: 0x600000265640>{number = 3, name = (null)}
2017-08-09 19:03:00.925 GCDdemo[7093:310765] 任务2------<NSThread: 0x608000462680>{number = 4, name = (null)}
2017-08-09 19:03:00.925 GCDdemo[7093:310767] 任务3------<NSThread: 0x600000272fc0>{number = 5, name = (null)}
2017-08-09 19:03:00.926 GCDdemo[7093:310764] 任务1------<NSThread: 0x600000265640>{number = 3, name = (null)}
2017-08-09 19:03:00.928 GCDdemo[7093:310765] 任务2------<NSThread: 0x608000462680>{number = 4, name = (null)}
2017-08-09 19:03:00.928 GCDdemo[7093:310767] 任务3------<NSThread: 0x600000272fc0>{number = 5, name = (null)}

  1. 并行队列 + 异步执行可以看出,除了主线程,程序有开启了3个线程,并且交替执行
  2. 另一方面可以看出,所有任务是在打印的syncConcurrent---beginsyncConcurrent---end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始异步执行
  • 串行队列 + 同步执行

不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务

    NSLog(@"asyncConcurrent---begin");
    
    //串行队列方法
    dispatch_queue_t  queue=dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    
    //创建同步执行任务
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
   
    NSLog(@"asyncConcurrent---end");
    

输出结果

2017-08-09 19:11:56.395 GCDdemo[7131:313993] asyncConcurrent---begin
2017-08-09 19:11:56.396 GCDdemo[7131:313993] 任务1------<NSThread: 0x6080002617c0>{number = 1, name = main}
2017-08-09 19:11:56.396 GCDdemo[7131:313993] 任务1------<NSThread: 0x6080002617c0>{number = 1, name = main}
2017-08-09 19:11:56.397 GCDdemo[7131:313993] 任务2------<NSThread: 0x6080002617c0>{number = 1, name = main}
2017-08-09 19:11:56.397 GCDdemo[7131:313993] 任务2------<NSThread: 0x6080002617c0>{number = 1, name = main}
2017-08-09 19:11:56.398 GCDdemo[7131:313993] 任务3------<NSThread: 0x6080002617c0>{number = 1, name = main}
2017-08-09 19:11:56.398 GCDdemo[7131:313993] 任务3------<NSThread: 0x6080002617c0>{number = 1, name = main}
2017-08-09 19:11:56.400 GCDdemo[7131:313993] asyncConcurrent---end

  1. 串行队列 + 同步执行可以看到,所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行
  2. 同时我们还可以看到,所有任务都在打印的syncConcurrent---beginsyncConcurrent---end之间,这说明任务是添加到队列中马上执行的
  • 串行队列 + 异步执行

会开启新的线程(只会开启1条线程),但是因为任务是串行的,执行完一个任务,再执行下一个任务

 NSLog(@"asyncConcurrent---begin");
    
    //串行队列方法
    dispatch_queue_t  queue=dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    
    //创建异步执行任务
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
   
    NSLog(@"asyncConcurrent---end");
  1. 串行队列 + 异步执行可以看到,开启了一条新线程,但是任务还是串行,所以任务是一个一个执行。
  2. 另一方面可以看出,所有任务是在打印的syncConcurrent---beginsyncConcurrent---end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

主队列

  1. 所有放在主队列中的任务,都会放到主线程中执行
  2. 可使用dispatch_get_main_queue()获得主队列
  • 主队列 + 同步执行

互等卡住不可行(在主线程中调用)

    NSLog(@"asyncConcurrent---begin");
    
    //主队列
    dispatch_queue_t queue=dispatch_get_main_queue();

    
    //创建同步执行任务
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
   
    NSLog(@"asyncConcurrent---end");
    
    

输出结果

2017-08-09 19:25:43.049 GCDdemo[7199:320037] asyncConcurrent---begin

程序为什么会挂了?

崩了,主要原因是当前的队列是主队列,主队列中加了一个同步执行的任务。这个同步执行的任务必须要这个主队列执行完成,才执行,而主队列要执行完成必须要同步任务执行完成走主队列,也就是主队列===同步任务,同步任务====主队列,也就互相等,然后就GG了

  • 主队列 + 异步执行

只在主线程中执行任务,执行完一个任务,再执行下一个任务

//主队列
    dispatch_queue_t queue=dispatch_get_main_queue();

    
    //创建异步执行任务
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
   
    NSLog(@"asyncConcurrent---end");

输出结果

asyncConcurrent---begin
2017-08-09 19:36:08.925 GCDdemo[7255:323818] asyncConcurrent---end
2017-08-09 19:36:08.942670 GCDdemo[7255:323818] subsystem: com.apple.BackBoardServices.fence, category: App, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0
2017-08-09 19:36:09.522 GCDdemo[7255:323818] 任务1------<NSThread: 0x600000069640>{number = 1, name = main}
2017-08-09 19:36:09.523 GCDdemo[7255:323818] 任务1------<NSThread: 0x600000069640>{number = 1, name = main}
2017-08-09 19:36:09.524 GCDdemo[7255:323818] 任务2------<NSThread: 0x600000069640>{number = 1, name = main}
2017-08-09 19:36:09.524 GCDdemo[7255:323818] 任务2------<NSThread: 0x600000069640>{number = 1, name = main}
2017-08-09 19:36:09.525 GCDdemo[7255:323818] 任务3------<NSThread: 0x600000069640>{number = 1, name = main}
2017-08-09 19:36:09.525 GCDdemo[7255:323818] 任务3------<NSThread: 0x600000069640>{number = 1, name = main}

  1. 可以看出任务都是在主线程中执行完成,并没有开启新的线程,主要原因是主队列
  2. 从答应的结果看出先打印出syncConcurrent---begin和""syncConcurrent---end"",说明任务并不是马上执行,而是先把任务放入队列中再执行

5.GCD线程通讯

在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
        
        // 回到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"任务2-------%@",[NSThread currentThread]);
        });
    });

输出结果

2017-08-09 19:54:23.527 GCDdemo[7384:332728] 任务1------<NSThread: 0x600000070800>{number = 4, name = (null)}
2017-08-09 19:54:23.528 GCDdemo[7384:332728] 任务1------<NSThread: 0x600000070800>{number = 4, name = (null)}
2017-08-09 19:54:24.065 GCDdemo[7384:332634] 任务2-------<NSThread: 0x608000067e40>{number = 1, name = main}
可以看出开启了其他线程执行任务,最后执行主线程

6. GCD其他方法

  • GCD的界限方法

dispatch_barrier_async
有时候我们需要这种操作,我要分几组异步操作,第一组执行完了,第二组再执行,然后第三组再执行需要这种分组的操作就需要dispatch_barrier_async


//并行队列
    dispatch_queue_t queue=dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);

    //创建异步执行任务
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1------%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2------%@",[NSThread currentThread]);
        }
    });
    
    
    dispatch_barrier_sync(queue, ^{
        
        NSLog(@"==========分割线111111");
    });
    
    
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3------%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务4------%@",[NSThread currentThread]);
        }
    });
    
    dispatch_barrier_sync(queue, ^{
        
        NSLog(@"==========分割线22222");
    });
    
    
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务5------%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务6------%@",[NSThread currentThread]);
        }
    });
    
   
    NSLog(@"asyncConcurrent---end");

输出结果

2017-08-09 20:09:14.558 GCDdemo[7497:339896] asyncConcurrent---begin
2017-08-09 20:09:14.559 GCDdemo[7497:340004] 任务2------<NSThread: 0x60800007c1c0>{number = 3, name = (null)}
2017-08-09 20:09:14.559 GCDdemo[7497:340007] 任务1------<NSThread: 0x600000273440>{number = 4, name = (null)}
2017-08-09 20:09:14.559 GCDdemo[7497:340004] 任务2------<NSThread: 0x60800007c1c0>{number = 3, name = (null)}
2017-08-09 20:09:14.560 GCDdemo[7497:340007] 任务1------<NSThread: 0x600000273440>{number = 4, name = (null)}
2017-08-09 20:09:14.560 GCDdemo[7497:339896] ==========分割线111111
2017-08-09 20:09:14.561 GCDdemo[7497:340007] 任务3------<NSThread: 0x600000273440>{number = 4, name = (null)}
2017-08-09 20:09:14.561 GCDdemo[7497:340004] 任务4------<NSThread: 0x60800007c1c0>{number = 3, name = (null)}
2017-08-09 20:09:14.561 GCDdemo[7497:340007] 任务3------<NSThread: 0x600000273440>{number = 4, name = (null)}
2017-08-09 20:09:14.562 GCDdemo[7497:340004] 任务4------<NSThread: 0x60800007c1c0>{number = 3, name = (null)}
2017-08-09 20:09:14.562 GCDdemo[7497:339896] ==========分割线22222
2017-08-09 20:09:20.701 GCDdemo[7497:339896] asyncConcurrent---end

可以看出先执行分割线之前的,再执行分割线后的

  • GCD的延时执行方法

dispatch_after
当我们需要延迟执行一段代码时,就需要用到GCD的dispatch_after方法。

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        //10秒后执行
        NSLog(@"啊哈哈");
        
    });
  • GCD的快速迭代方法

** dispatch_apply**
通常便利一个数组什么的都是for循环,使用** dispatch_apply**更加高效

    
    NSArray  *array=@[@"哈哈",@"呵呵"];
    
    dispatch_apply(array.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
        
        
        NSLog(@"======%@",array[index]);
        
    });

输出结果

2017-08-09 20:29:55.843 GCDdemo[7895:354929] ======呵呵
2017-08-09 20:29:55.843 GCDdemo[7895:354823] ======哈哈

  • GCD单例

** dispatch_once**
我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});
  • GCD的队列组

dispatch_group
有时候我们会有这样的操作,分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组

  1. 先把线程放在队列里面,
  2. 再把队列放在队列组
    3.调用队列组的dispatch_group_notify回到主线程执行操作
//队列组
    dispatch_group_t group=dispatch_group_create();
    
    
    //异步耗时操作
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            for (int i = 0; i < 2; ++i) {
                NSLog(@"任务1------%@",[NSThread currentThread]);
            }
        });
        
    });
    
    //异步耗时操作
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            
            for (int i = 0; i < 2; ++i) {
                NSLog(@"任务2------%@",[NSThread currentThread]);
            }
        });
        
    });
    
    
    //队列组回到主线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        
        NSLog(@"回答主线程");
        
    });

输出结果

2017-08-09 20:43:44.710 GCDdemo[8076:362721] 任务2------<NSThread: 0x60800007aa80>{number = 4, name = (null)}
2017-08-09 20:43:44.710 GCDdemo[8076:362722] 任务1------<NSThread: 0x60800007a8c0>{number = 3, name = (null)}
2017-08-09 20:43:44.711 GCDdemo[8076:362721] 任务2------<NSThread: 0x60800007aa80>{number = 4, name = (null)}
2017-08-09 20:43:44.711 GCDdemo[8076:362722] 任务1------<NSThread: 0x60800007a8c0>{number = 3, name = (null)}
2017-08-09 20:43:44.717 GCDdemo[8076:362623] 回答主线程

小总结

本文借鉴了不是前辈的经验,一点小总结希望可以帮助各位兄弟。一起快乐学习!!!


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

推荐阅读更多精彩内容

  • 本文首发于我的个人博客:「程序员充电站」[https://itcharge.cn]文章链接:「传送门」[https...
    ITCharge阅读 347,198评论 308 1,924
  • 很长一段时间了,一个人呆着,就想东想西,各种想法在脑袋里乱撞,让我不得安生。试图控制住这些想法,他们却像受惊的鸟,...
    白螺黑沙阅读 241评论 0 1
  • 梦里叶舞知多少 见君辗转情何了 舍得那叶风飘飘 宁可怀柔勿知晓 天自煞秋不在高 地作何解叶自敲 作别昔日一语妙 征...
    化骨真人阅读 168评论 0 1
  • 古典老师在得到里专门做了个专栏《超级个体》,老师以独特的视角全面系统地阐述个体如何快速成长。专栏通过图文、音频的形...
    云川橙子1阅读 191评论 0 0
  • 熊二说要请我看电影,选择了嘉年华。 知道电影名字时,我没有去看电影介绍,仅凭对字面理解,以为这是一部欢快的影片。 ...
    花儿姐姐1977阅读 166评论 1 0