GCD

参考文档1
参考文档2
参考文档3


参考文档4
参考文档5

Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。

1. GCD术语

1.1串行Serial

任务串行执行时,一次只能执行一个任务。

1.2并行Parallelism

并行指多个任务同时执行
多核设备可以同时执行多个线程;单核设备如果想要并行执行任务,就需要运行一个线程,执行环境切换(也称上下文切换,context switch),运行另外一个线程或进程(process),这通常很快发生,足以形成并行执行的错觉

1.3并发

并发指task同时(simultaneously)发生,可以被同时执行,但具体是否同时执行由GCD根据当前负载决定

1.4同步Synchronous

同步执行会等待提交的task执行完毕后,再继续执行后面任务。我们平常调用的方法都是同步方法,如第一行调用doSomething:方法,程序执行第二行时,doSomething:方法肯定执行完毕了。同步执行一些耗时操作时会堵塞当前线程。调用dispatch_sync函数并将任务提交到当前队列(即dispatch_sync函数所在队列)会导致死锁。
因为是同步调用,目标队列不会复制块(Block_copy),而只引用块。为提高性能,同步函数一般尽可能在当前线程调用块

1.5异步Asynchronous

异步指调用任务后立即返回,不需要等待任务执行。异步不会堵塞当前线程

1.6死锁Dead Lock

两个或多个任务、线程都在等待对方完成操作。第一个任务无法完成,因为它在等待第二个任务完成。第二个任务无法完成,因为它在等待第一个任务完成。两个任务相互等待,这就形成了死锁。

1.7线程安全Thread Safe

线程安全的代码可以在多个线程或并发任务中同时执行。非线程安全的代码一次只能在一个环境中运行。例如,NSArray是线程安全的,可以同时在多个线程或并发任务中执行。而NSMutableArray不是线程安全的,一次只能在一个环境中运行

1.8环境切换Context Switch

Context switch指当在同一个处理器处理不同线程时,存储和恢复执行状态的过程。编写多任务应用程序时,进行上下文切换非常常见,但会产生额外的开销。

2. 两种队列

GCD提供的调度队列( dispatch queue)是一个类似于对象的结构,用于管理向其提交的任务。
所有的dispatch queue都是先进先出(first in, first out. 简称FIFO)的数据结构,因此,队列中任务运行顺序与添加顺序一致,即第一个进入队列的任务,第一个被执行;第二个进入队列的任务,第二个被执行;以此类推。

所有dispatch queue自身都是线程安全的
GCD的队列分为串行队列(Serial Queues)和并发队列(Concurrent Queues)两种

两种队列都用dispatch_queue_create创建,传入两上参数,
第1个参数是队列名称(唯一标识符),debugger和性能工具会显示此处队列名称以帮助跟踪任务执行情况,可为空。
第2个参数用于标识队列类型,串行/并发。

//串行队列的创建
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
//并发队列的创建
dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);


//主队列(Main Dispatch Queue)
//主队列是一种特殊的串行队列
**所有放在主队列中的任务,都会放到主线程中执行**
dispatch_queue_t mainQueue = dispatch_get_main_queue();

//全局并行队列(Global Dispatch Queue)
//第1个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。
//第2个参数暂时没用,用0即可。
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

队列是指执行任务的等待队列,即用来存放任务的队列。
是一种特殊的线性表,采用 FIFO(先进先出)的原则,由GCD内部自行控制。

同步执行和异步执行 指的都是对一个已经创建的队列,把任务(代码/代码块)添加进去的方式。

同步执行和异步执行区别:

  1. 会不会阻塞当前线程,是否等待队列的任务执行结束
  2. 是否具备开启新线程的能力。

**

  • 串行队列(Serial Dispatch Queue)
    每次只执行一个任务,让任务一个接着一个地执行。一个任务执行完毕后,再执行下一个任务。
    只开启一个新线程(主线程则不开启新线程,在当前线程执行任务)

  • 并发队列(Concurrent Dispatch Queue)
    可以让多个任务并发(同时)执行。
    可以开启多个线程,并且同时执行任务。

  • 同步执行(sync)
    同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
    只能在当前线程中执行任务,不具备开启新线程的能力。

  • 异步执行(async)
    异步添加任务到指定的队列中,它不会做任何等待,
    可以继续执行任务。
    可以在新的线程中执行任务,具备开启新线程的能力。

2. 任务的创建

GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。

// 同步执行任务创建方法
dispatch_sync(queue, ^{
    // 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
    // 这里放异步执行任务代码
});
1. 异步执行 + 并行队列
/**
 * 异步执行 + 并行队列
 * 特点:可以开启多个线程,任务交替(同时)执行。
 */
- (void)asyncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"asyncConcurrent---end");
}

结果 :
所有任务是在打印的
syncConcurrent---begin和syncConcurrent---end之后才执行的。
说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务

总结:
异步执行具备开启新线程的能力,所以除了当前主线程,系统又开启了其它线程,任务之间不需要排队,且是交替/同时执行的。

2. 异步执行 + 串行队列
/**
 * 异步执行 + 串行队列
 * 特点:会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。
 */
- (void)asyncSerial {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncSerial---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"asyncSerial---end");
}

结果
所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。

总结
异步执行具备开启新线程的能力,但由于是串行队列所以只开启一个线程。任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)

3. 同步执行 + 并行队列
/**
 * 同步执行 + 并行队列
 * 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。
 **/
- (void)syncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncConcurrent---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"syncConcurrent---end");
}

结果 :
所有任务都在打印的syncConcurrent---begin
和syncConcurrent---end之间执行的(同步任务需要等待队列的任务执行结束),所有 任务按顺序执行的

总结:
所有任务都是在当前主线程中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)。
按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为队列本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。

可见并发队列的并发功能只有在异步(dispatch_async)函数下才有效。

4. 同步执行 + 串行队列
/**
 * 同步执行 + 串行队列
 * 特点:不会开启新线程,在当前线程执行任务。
   任务是串行的,执行完一个任务,再执行下一个任务。
 */
- (void)syncSerial {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncSerial---begin");
    
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"syncSerial---end");
}

结果
所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)

总结:
这里的执行原理和步骤图跟“同步执行+并发队列”是一样的,只要是同步执行就没法开启新的线程,所以多个任务之间也一样只能按顺序来执行
任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

5. 同步执行+主队列
/**
 * 同步执行 + 主队列
 * 特点(主线程调用):互等卡主不执行。
 * 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。
 */
- (void)syncMain {
    
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"syncMain---begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"syncMain---end");
}

总结:
主线程中同步执行+主队列
在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中。而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程处理完syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。
现在的情况就是syncMain任务和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且syncMain---end也没有打印。

6.异步执行 + 主队列
/**
 * 异步执行 + 主队列
 * 特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务
 */
- (void)asyncMain {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncMain---begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"asyncMain---end");
}

结果
所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)
总结:
所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。
任务是按顺序执行的(因为主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。


任务依赖

一般情况下,异步添加到并行队列中的任务,我们是没法控制执行顺序的,或者说也是按FIFO顺序执行,但执行完成的顺序确无法控制。下面是二般情况:

1.Barrier


barrier只能在自己创建的串行/并行队列中才有效

dispatch_queue_t globalQueue = dispatch_queue_create("ConcurrentQ", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之前的任务1");
        
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之前的任务2");
    });
    /***************************/
    //使用dispatch_barrier将任务添加到队列后,
    //任务会在前面任务全部执行完后执行
    //且任务在执行过程中,其它任务无法执行    
    dispatch_barrier_async(globalQueue, ^{
        
        NSLog(@"barrier ConQ 1");
    });
    /***************************/
    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之后的任务1");
        
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"barrier之后的任务2");
    });
    /***************************/
    
    dispatch_barrier_async(globalQueue, ^{
        
        NSLog(@"barrier ConQ 2");
    });
    
    dispatch_barrier_async(globalQueue, ^{
        NSLog(@"barrier ConQ 3");
    });
   
  //由于是异步添加任务,所以不用等任务执行完,
  //有可能会被首先执行打印 
    NSLog(@"当前队列的最后一个任务");

2.GCD group

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();

 ---------------dipatch_group------------
//barrier将任务添加到一个队列中,可以同或异步添加
//group将伤添加到一个组当中,再将组放入队列,异步添加

    dispatch_group_async(group, globalQueue, ^{
       
        NSLog(@"globalQueue1");
    });

    dispatch_group_async(group, globalQueue, ^{
    // sleep是全局休眠定时器
    // group_wait只阻塞当前group block,所以3会先执行
        dispatch_group_wait(gp, dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC));
        NSLog(@"globalQueue2");
    });

    dispatch_group_async(group, globalQueue, ^{
     
        NSLog(@"globalQueue3");
    });

    /*************当group所有任务完成后,会发送完成通知******************/
    dispatch_group_notify(group, globalQueue, ^{
        NSLog(@"1.2.3 已完成");
    });

    //由于是异步添加到并行队列,这个任务有可能被最先执行完
    dispatch_async(globalQueue, ^{
        
        NSLog(@"globalQueue4");
    });

或者

项目需求是AB进行网络请求,数据返回后,回主线程合并,但对网络请求而言,请求发出即算执行完毕。所以上面的写法就不更适合。

 dispatch_group_t group = dispatch_group_create();

---------------dipatch_group------------

    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"任务一完成");
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        NSLog(@"任务二完成");
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务完成");
    });

网络请求开始前dispatch_group_enter(group) ,请求成功或失败后都执行dispatch_group_leave(group)。当所有的enter都leave后会触发通知。

3.Semaphore

信号量大于0时执行后面代码,等于0时等待
dispatch_semaphore_create 创建一个semaphore 
dispatch_semaphore_signal 发送一个信号,让信号总量+1
dispatch_semaphore_wait 信号量为0时,等待信号;信号量大于0时,让信号量-1,并执行后面代码。

//创建信号量
dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
//添加任务1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
        
        NSLog(@"第一个任务");
        dispatch_semaphore_signal(semaphore1);//信号量+1
    });


//添加任务2    
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //判断信号量
        //如果为0,则根据传入的时间等待
        //如果不为0,则-1执行
        dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
      
        NSLog(@"第二个任务");
        dispatch_semaphore_signal(semaphore2);//信号量+1
    });

///判断信号量  
dispatch_async(dispatch_get_global_queue(0, 0), ^{
       dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
        NSLog(@"这是最后一个任务");
    });

在网络请求中所有请求加异步加入到并行队列后,成功/失败都要semaphore_signal,然后在异步并行队列中判断是否可以执行

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

dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);

dispatch_async(queue,^{
    //第一个网络请求
    ...  ...
    dispatch_semaphore_signal(semaphore1);
}
dispatch_async(queue,^{
    //第二个网络请求
      ... ...
    dispatch_semaphore_signal(semaphore2);
}
//semphore_wait并发队列中执行
dispatch_async(queue,^{
     dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
     dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);

     dispatch_async(dispatch_get_main_queue(), ^{
      //刷新UI   
     });
 });

PS:

dispatch_after是延迟提交,不是延迟运行,所以在串行队列中,先提交先运行。

//串行队列
 dispatch_queue_t queue = dispatch_queue_create("serialQ", DISPATCH_QUEUE_SERIAL);

    NSLog(@"Begin add block...");
    //提交一个block
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:10];
        NSLog(@"First block done...");
    });
    
    //5 秒以后提交block
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
    //dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
    //dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);同样表示1秒
        NSLog(@"After...");
    });

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

推荐阅读更多精彩内容

  • 文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。通过本文,您将了解到: 1. GCD 简介 2. G...
    晓_我想去环游世界阅读 1,145评论 2 8
  • 文化普及第一篇 霸者,伯也。这是霸字的一种释义,伯,也就是兄长。春秋战国时期,诸侯争霸,意思就是说,各路诸侯争着当...
    bella火火火火阅读 242评论 0 0
  • 每一对初婚的恋人,当初都彼此赤诚以待,不在乎对方缺点,接受对方所有一切,祸福与共,风雨同舟。 婚外情中的人们不能做...
    运安阁主阅读 494评论 0 0
  • 在办公室拖地时,我感到很喜悦,因为想到大家踏进我清新爽洁的工作区域会产生无比的愉悦感,而且当自己身处其中,也会有很...
    静宇蓝歆阅读 205评论 0 1
  • 不得不说,我经常忘记自己的年龄,也有人时常夸奖我并不像所谓的80后,但是我知道,真正与年龄不符的,是心智! 嫁给先...
    罂粟花儿开阅读 174评论 0 0