iOS - GCD

GCD
队列

  • 串行队列
  • 并行队列
  • 全局队列
  • 主队列

几个容易混淆的概念
dispatch_barrier_async栅栏函数
dispatch_group_t队列组
dispatch_semaphore_t信号量
dispatch_apply 快速迭代
dispatch_suspend和dispatch_resume
例:

GCD:

GCD全称 Grand Central Dispatch,我们通俗的翻译叫牛逼的中心调度。

队列

先说下GCD中的队列 dispatch_queue_t

串行队列: 同步执行,在当前线程执行
并行队列: 可由多个线程异步执行,但任务的取出还是FIFO的

    //创建队列
    //参数1: 队列名
    //参数2: 队列类型  DISPATCH_QUEUE_SERIAL |  NULL   串行队列,
                     DISPATCH_QUEUE_CONCURRENT       并发队列
    dispatch_queue_t sky_queue = dispatch_queue_create("队列名字", NULL);

全局队列: 顾名思义就是全局队列,由系统创建,属于并行队列

    //全局队列
    /*
    参数1 : iOS8以前是优先级, iOS8以后是服务质量
    iOS8以前
    *  - DISPATCH_QUEUE_PRIORITY_HIGH          高优先级 2
    *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      默认的优先级 0
    *  - DISPATCH_QUEUE_PRIORITY_LOW:          低优先级 -2
    *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:
    
    iOS8以后
    *  - 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: 没用过一般传 0
    */
    //例子
    dispatch_queue_t sky_global_queue = dispatch_get_global_queue(0, 0);

主队列: 主队列属于串行队列. 运行在主线程(UI线程)

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

几个容易混淆的概念

  1. **串行队列,同步执行: ** 在当前线程上顺序执行任务,并不会开启新线程.
  1. 串行队列,异步执行: 在不是主线程的另一个线程上顺序执行任务.(因为是异步执行,如果多线程的话不能保证顺序执行,所以是一个线程。)
  2. 并发队列,同步执行 : 在当前线程上顺序执行任务,并不会开启新线程. (同串行队列同步执行)
  3. 并发队列,异步执行: 给每一个任务开启一个线程去执行(因为队列中的所有任务都是异步的)分别执行每个任务.每个任务都是从头开始的.哪个任务先结束由任务本身决定,但是系统都会给每一个任务一个单独的线程去执行。

看看代码:


//串行 同步
- (void)sky_syncSerial
{
    //同步+串行 不会开始新线程.   等到同步函数调用完成之后才会执行后面的代码
    dispatch_queue_t sky_sync_serial_queue = dispatch_queue_create("com.sky.queue", DISPATCH_QUEUE_SERIAL);// DISPATCH_QUEUE_SERIAL == NULL
    
    dispatch_sync(sky_sync_serial_queue, ^{
        NSLog(@"1 = %@",[NSThread currentThread]);
    });
    
    dispatch_sync(sky_sync_serial_queue, ^{
        NSLog(@"2 = %@",[NSThread currentThread]);
    });
    
    dispatch_sync(sky_sync_serial_queue, ^{
        NSLog(@"3 = %@",[NSThread currentThread]);
    });

    NSLog(@"4 ");
    
    /* 
     运行结果:
     2017-01-26 16:16:40.424 GCDNotes[93061:9398100] 1 = <NSThread: 0x608000070100>{number = 1, name = main}
     2017-01-26 16:16:40.424 GCDNotes[93061:9398100] 2 = <NSThread: 0x608000070100>{number = 1, name = main}
     2017-01-26 16:16:40.424 GCDNotes[93061:9398100] 3 = <NSThread: 0x608000070100>{number = 1, name = main}
     2017-01-26 16:16:40.425 GCDNotes[93061:9398100] 4
     */
}

//串行 异步
- (void)sky_asyncSerial
{
   //在不是主线程的另一个线程顺序执行任务. 因为是异步所以开启新线程. 因为是串行所以是一个线程.
    dispatch_queue_t sky_global_queue = dispatch_queue_create("com.sky.queue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(sky_global_queue, ^{
        NSLog(@"1 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_global_queue, ^{
        NSLog(@"2 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_global_queue, ^{
        NSLog(@"3 = %@",[NSThread currentThread]);
    });
    
    NSLog(@"4 ");
    /*
     运行结果:
     2017-01-26 18:56:19.132 GCDNotes[93256:9704465] 4
     2017-01-26 18:56:19.132 GCDNotes[93256:9704622] 1 = <NSThread: 0x60800026a500>{number = 5, name = (null)}
     2017-01-26 18:56:19.133 GCDNotes[93256:9704622] 2 = <NSThread: 0x60800026a500>{number = 5, name = (null)}
     2017-01-26 18:56:19.133 GCDNotes[93256:9704622] 3 = <NSThread: 0x60800026a500>{number = 5, name = (null)}
     */
}

//并发 同步
- (void)sky_syncConCurrent
{
    //同步 + 并发 : 在同一个线程里面.但不是主线程,如果同步在主线程里面会造成死锁.
    //原因: 主队列,如果主线程正在执行代码,就不调度任务;同步执行:一直执行第一个任务直到结束。两者互相等待造成死锁
    //错误代码示例:
    /*
    dispatch_queue_t sky_main_queue = dispatch_get_main_queue();
    dispatch_sync(sky_main_queue, ^{
        //发送错误,不会走下面的代码 ~
        NSLog(@"erroe = %@",sky_main_queue);
    });
    */
    
    dispatch_queue_t sky_sync_Concurrent_queue = dispatch_queue_create("com.sky.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(sky_sync_Concurrent_queue, ^{
        NSLog(@"1 = %@",[NSThread currentThread]);
    });
    
    dispatch_sync(sky_sync_Concurrent_queue, ^{
        NSLog(@"2 = %@",[NSThread currentThread]);
    });
    
    dispatch_sync(sky_sync_Concurrent_queue, ^{
        NSLog(@"3 = %@",[NSThread currentThread]);
    });
    
    NSLog(@"4 ");
    
    /*
     运行结果:
     2017-01-26 19:01:34.868 GCDNotes[93272:9727007] 1 = <NSThread: 0x60000006a000>{number = 1, name = main}
     2017-01-26 19:01:34.869 GCDNotes[93272:9727007] 2 = <NSThread: 0x60000006a000>{number = 1, name = main}
     2017-01-26 19:01:34.869 GCDNotes[93272:9727007] 3 = <NSThread: 0x60000006a000>{number = 1, name = main}
     2017-01-26 19:01:34.869 GCDNotes[93272:9727007] 4
     */
}

//并发 异步
- (void)sky_asyncConCurrent
{
    //开启新线程. 有多少任务开启多少线程.(如果任务完成,其它的任务就会使用已经完成的.没有完成就会继续开启线程)
    dispatch_queue_t sky_async_concurrent_queue = dispatch_queue_create("com.sky.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(sky_async_concurrent_queue, ^{
       NSLog(@"1 = %@",[NSThread currentThread]);
    });

    dispatch_async(sky_async_concurrent_queue, ^{
        NSLog(@"2 = %@",[NSThread currentThread]);
    });

    dispatch_async(sky_async_concurrent_queue, ^{
        NSLog(@"3 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_async_concurrent_queue, ^{
        NSLog(@"4 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_async_concurrent_queue, ^{
        NSLog(@"5 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_async_concurrent_queue, ^{
        NSLog(@"6 = %@",[NSThread currentThread]);
    });

    NSLog(@"7");
    
    /*
     运行结果:
     2017-01-26 19:07:04.372 GCDNotes[93315:9751108] 3 = <NSThread: 0x6000002638c0>{number = 5, name = (null)}
     2017-01-26 19:07:04.372 GCDNotes[93315:9751058] 7
     2017-01-26 19:07:04.372 GCDNotes[93315:9751129] 2 = <NSThread: 0x60000007a600>{number = 4, name = (null)}
     2017-01-26 19:07:04.372 GCDNotes[93315:9751107] 1 = <NSThread: 0x608000074940>{number = 3, name = (null)}
     2017-01-26 19:07:04.372 GCDNotes[93315:9751110] 4 = <NSThread: 0x600000264300>{number = 6, name = (null)}
     2017-01-26 19:07:04.372 GCDNotes[93315:9751394] 5 = <NSThread: 0x600000264380>{number = 7, name = (null)}
     2017-01-26 19:07:04.372 GCDNotes[93315:9751108] 6 = <NSThread: 0x6000002638c0>{number = 5, name = (null)}
     */
}

dispatch_barrier_async栅栏函数

  • barrier_async只有前面的任务执行并完成后才会执行. 如果后面还有任务要等到barrier_async执行完成之后才会继续执行.
  • 必须使用自定义的队列.否则即使加上也和全局队列效果一样,也就是在全局队列中barrier_async会和其它普通的任务一样.不会等到他之前的所有任务执行完成之后再执行.
  • dispatch_barrier_sync会在主线程中执行任务。
    dispatch_queue_t sky_queue = dispatch_queue_create("com.sky.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(sky_queue, ^{
        NSLog(@"1 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_queue, ^{
        NSLog(@"2 = %@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(sky_queue, ^{
        NSLog(@"barrier  %@ ",[NSThread currentThread]);
    });
    
    dispatch_async(sky_queue, ^{
        NSLog(@"3 = %@",[NSThread currentThread]);
    });
    
    dispatch_async(sky_queue, ^{
        NSLog(@"4 = %@",[NSThread currentThread]);
    });
 
    dispatch_async(sky_queue, ^{
        NSLog(@"5 = %@",[NSThread currentThread]);
    });
    /**
     运行结果:
     2017-01-26 19:42:09.012 GCDNotes[93364:9892792] 2 = <NSThread: 0x60800026d1c0>{number = 4, name = (null)}
     2017-01-26 19:42:09.012 GCDNotes[93364:9892793] 1 = <NSThread: 0x600000076b40>{number = 3, name = (null)}
     2017-01-26 19:42:09.012 GCDNotes[93364:9892793] barrier  <NSThread: 0x600000076b40>{number = 3, name = (null)}
     2017-01-26 19:42:09.013 GCDNotes[93364:9892792] 3 = <NSThread: 0x60800026d1c0>{number = 4, name = (null)}
     2017-01-26 19:42:09.013 GCDNotes[93364:9892795] 4 = <NSThread: 0x608000276180>{number = 5, name = (null)}
     2017-01-26 19:42:09.013 GCDNotes[93364:9892793] 5 = <NSThread: 0x600000076b40>{number = 3, name = (null)}
     */

dispatch_group_t队列组

  • 使用场景: 异步执行2个操作,等都执行完成之后,回到主线程继续执行.(比如俩图片下载完后合并成一个,或俩网络请求完成之后去刷新列表等~(栅栏函数也可以 ))
  • 设置阻塞组(超时)任务一定时间. dispatch_group_wait(group, DISPATCH_TIME_FOREVER);第一个参数是需要阻塞的组,第二个是阻塞的时间.
  • dispatch_group_notify 当group关联的block执行完毕后,就调用这个block

系统管理队列组和手动管理队列组. 两个是等价的. 手动的enter和leave必须成对使用.

//系统管理
dispatch_group_async(group, queue, ^{
      //...
 });
//手动管理
dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // ...
        dispatch_group_leave(group);
    });
    //线程队列组
    dispatch_queue_t sky_queue = dispatch_queue_create("com.sky.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t sky_group = dispatch_group_create();
    
    //线程组的用法和原理:可以给一个任务组设置一个阻塞(超时)时间,如
      果给一个任务组的前两个任务设置一个超时时间那么如果这前两个任务
      提前执行结束了,也就是在设定的阻塞时间内完成了任务,那么这个阻塞
      就会放行下面的任务去执行任务,如果超过了超时时间那么这个阻塞也
      会放行,让下面的任务可以执行.阻塞时间并不能杀死任务,也就是说如果
      你设定了阻塞(超时)时间,阻塞时间前的任务在阻塞放行前并没有执行完
      成,然后任务就被放行了,系统继续往下执行,但是这时候阻塞时间前的任
      务也会执行完成如果你不手动杀死它的话.
    //dispatch_group_wait(sky_group, 3); //第一个参数 阻塞的组 第二个
      参数 阻塞时间
    
    dispatch_group_async(sky_group, sky_queue, ^{
        NSLog(@" 1 %@ ",[NSThread currentThread]);
    });
    
    dispatch_group_async(sky_group, sky_queue, ^{
        NSLog(@" 2 %@ ",[NSThread currentThread]);
    });
    
    dispatch_group_async(sky_group, sky_queue, ^{
        [NSThread sleepForTimeInterval:3];//等待三秒
        NSLog(@" 3 %@ ",[NSThread currentThread]);
    });

    dispatch_group_async(sky_group, sky_queue, ^{
        NSLog(@" 4 %@ ",[NSThread currentThread]);
    });

       //回到主线程
    dispatch_group_notify(sky_group, sky_queue, ^{
        //监测方法在最后一个任务执行的线程中执行
        NSLog(@"回到主线程了 ~ %@" , [NSThread currentThread]);
    });
    
    /**
     运行结果:
     2017-01-26 20:26:27.768 GCDNotes[93449:10087233]  1 <NSThread: 0x60000026b6c0>{number = 4, name = (null)}
     2017-01-26 20:26:27.768 GCDNotes[93449:10087235]  2 <NSThread: 0x600000260f40>{number = 3, name = (null)}
     2017-01-26 20:26:27.768 GCDNotes[93449:10087330]  4 <NSThread: 0x600000271cc0>{number = 5, name = (null)}
     2017-01-26 20:26:30.769 GCDNotes[93449:10087232]  3 <NSThread: 0x608000260f80>{number = 6, name = (null)}
     2017-01-26 20:26:30.770 GCDNotes[93449:10087232] 回到主线程了 ~ <NSThread: 0x608000260f80>{number = 6, name = (null)}
     */

dispatch_semaphore_t信号量

信号量是用于控制并发数量的,所以只用在全局队列和并行队列中.

  1. 创建信号量,可以设置信号量的资源数。0表示没有资源,调用dispatch_semaphore_wait会立即等待。
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
  1. 等待信号,可以设置超时参数。该函数返回0表示得到通知,非0表示超时。
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
  2. 通知信号,如果等待线程被唤醒则返回非0,否则返回0。
    dispatch_semaphore_signal(semaphore);
    //创建队列组
    dispatch_group_t sky_group = dispatch_group_create();
    //创建信号为3的信号量
    dispatch_semaphore_t sky_semaphore = dispatch_semaphore_create(3);
    
    dispatch_queue_t sky_global = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    for (int i = 0 ; i < 10 ; i ++)
    {
        //设置等待信号 DISPATCH_TIME_FOREVER(永远) | DISPATCH_TIME_NOW(现在)
        dispatch_semaphore_wait(sky_semaphore, DISPATCH_TIME_FOREVER);
        
        //
        dispatch_group_async(sky_group, sky_global, ^{
            NSLog(@"i = %d : %@",i , [NSThread currentThread]);
            [NSThread sleepForTimeInterval:1];
            //设置通知信号
            dispatch_semaphore_signal(sky_semaphore);
        });
    }
    //通知主线程
    dispatch_group_notify(sky_group, sky_global, ^{
        NSLog(@"xxxxoooo");
    });
    /*
     运行结果:
     2017-01-26 21:10:53.006 GCDNotes[93525:10184012] i = 2 : <NSThread: 0x608000460fc0>{number = 5, name = (null)}
     2017-01-26 21:10:53.006 GCDNotes[93525:10184037] i = 0 : <NSThread: 0x60800026a040>{number = 3, name = (null)}
     2017-01-26 21:10:53.006 GCDNotes[93525:10184011] i = 1 : <NSThread: 0x608000279440>{number = 4, name = (null)}
     2017-01-26 21:10:54.009 GCDNotes[93525:10184037] i = 4 : <NSThread: 0x60800026a040>{number = 3, name = (null)}
     2017-01-26 21:10:54.009 GCDNotes[93525:10184012] i = 3 : <NSThread: 0x608000460fc0>{number = 5, name = (null)}
     2017-01-26 21:10:54.009 GCDNotes[93525:10184011] i = 5 : <NSThread: 0x608000279440>{number = 4, name = (null)}
     2017-01-26 21:10:55.015 GCDNotes[93525:10184037] i = 8 : <NSThread: 0x60800026a040>{number = 3, name = (null)}
     2017-01-26 21:10:55.015 GCDNotes[93525:10184012] i = 6 : <NSThread: 0x608000460fc0>{number = 5, name = (null)}
     2017-01-26 21:10:55.015 GCDNotes[93525:10184011] i = 7 : <NSThread: 0x608000279440>{number = 4, name = (null)}
     2017-01-26 21:10:56.020 GCDNotes[93525:10184037] i = 9 : <NSThread: 0x60800026a040>{number = 3, name = (null)}
     2017-01-26 21:10:57.023 GCDNotes[93525:10184037] xxxxoooo
     */
    //结果分析: 来自: http://www.jianshu.com/p/ac11fe7ef78c
    /*
     首先这里面创建了一个组,这个组放在这里只是告诉你可以这么用,它不会影响信号量的功能.
     这里创建了一个并发为3的信号量然而for循环是10个任务,那么理论
     上10个任务会创建十个线程去执行,但是你信号量为3所以只能创建3
     个线程去执行前面10个任务,然后等待前3个任务执行完成了腾出来新
     的线程再去执行等待执行的.所以下面的线程 number 最大是4,当然这
     前提是你设置的超时时间大于任务执行完的时间.如果设置超时时间是
     0.1秒的话,任务等超时了还是会开启另外的线程去执行任务,这样就
     达不到控制并发量的要求了.
     */
    //理解信号量: 来自: http://www.jianshu.com/p/ac11fe7ef78c
    /*
     一般可以用停车来比喻:
     停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此
     时来了五辆车,那么就有一辆需要等待。信号量的值就相当于剩余车位
     的数目,dispatch_semaphore_wait函数就相当于来了一辆车,
     dispatch_semaphore_signal就相当于走了一辆车。停车位的剩余数目
     在初始化的时候就已经指明了(dispatch_semaphore_create(long 
     value)),调用一次dispatch_semaphore_signal,剩余的车位就增加
     一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;当剩
     余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等
     待。有可能同时有几辆车等待一个停车位。有些车主没有耐心,给自己
     设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就
     开进去停车。而有些车主就像把车停在这,所以就一直等下去。
     */

dispatch_apply 快速迭代

可以给定指定的次数将block追加到指定的Dispatch Queue中,并且等待全部结束处理执行。

    dispatch_queue_t sky_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    NSArray *array = @[@"北京",@"我爱你",@"济南大学的遗憾",@"在济南大学的操场望天空",@"济南大学的食堂",@"梦游济南大学"];
    //参数1: 要遍历的次数
    //参数2: 在那个线程中
    //参数3: 回调
    dispatch_apply(array.count, sky_queue, ^(size_t index) {
        NSLog(@"%@",array[index]);
    });
    
    NSLog(@"xxxooo");
    /*
     运行结果:
     2017-01-26 21:48:46.007 GCDNotes[93665:10365139] 在济南大学的操场望天空
     2017-01-26 21:48:46.007 GCDNotes[93665:10365141] 我爱你
     2017-01-26 21:48:46.007 GCDNotes[93665:10365138] 济南大学的遗憾
     2017-01-26 21:48:46.007 GCDNotes[93665:10364896] 北京
     2017-01-26 21:48:46.007 GCDNotes[93665:10365139] 济南大学的食堂
     2017-01-26 21:48:46.007 GCDNotes[93665:10365141] 梦游济南大学
     2017-01-26 21:48:46.007 GCDNotes[93665:10364896] xxxooo
     */
     //无序.

dispatch_suspend和dispatch_resume

NSOperationQueue有暂停(suspend)和恢复(resume)。其实GCD中的队列也有类似的功能。用法也非常简单:
dispatch_suspend(queue)//暂停某个队列
dispatch_resume(queue) //恢复某个队列
这些函数不会影响到队列中已经执行的任务,队列暂停后,已经添加到队列中但还没有执行的任务不会执行,直到队列被恢复。

例:

  1. 多线程中下载图片,回到主线程刷新UI
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
       
        //多线程执行 ~
        NSURL *imageUrl = [NSURL URLWithString:@"http://b.hiphotos.baidu.com/image/pic/item/6a63f6246b600c33fe5527171e4c510fd8f9a1c5.jpg"];
        NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
        UIImage *image = [UIImage imageWithData:imageData];
        
        /*
         注意:
         如果想等UI更新完毕再执行后面的代码, 那么使用同步函数
         如果不想等UI更新完毕就需要执行后面的代码, 那么使用异步函数
         */
        dispatch_sync(dispatch_get_main_queue(), ^{
            //同步回到主线程刷新ui
            self.imageView.image = image;
        });
        
    });
  1. GCD 延迟
     //延迟1秒
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
        //这里传入全局队列会在子线程中执行block,如果传入主队列就会在主线程中执行block
        // ...
    });
  1. 一次执行(一般用来实现单列和全局就调用一次代码的地方比如:时间计算那块.特别是在复杂的列表页面每次都去计算时间格式很消耗性能.加上这个滑动就会流畅很多.)
    //一次执行
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
    });

如果书写有误 望指出 ~

推荐:

多线程技术内幕
iOS多线程-从不会到熟练使用

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

推荐阅读更多精彩内容