iOS 多线程 GCD(二)

前言

本文主要接扫GCD的信号量相关的内容。

代码的下载地址demo;

1、信号量简介

此处省略一万字......

2、信号量的三个主要方法(函数)说明

dispatch_semaphore_create(long value);
方法作用:创建信号总量,即初始信号量允许的最大值,例如
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
参数:信号总量的初值,数据类型为long类型。
返回值:如果value小于0,创建的sem对象其实是NULL类型。

dispatch_semaphore_signal(dispatch_semaphore_t deem);
方法作用:发送信号量 ,例如
dispatch_semaphore_signal(sem);
参数: dispatch_semaphore_t对象,比如刚才创建的sem
返回值:返回值为long类型。
当返回值为0时,表示当前并没有线程等待其处理的信号量,其处理的信号总量增加1。
当返回值不为0时,表示其当前有一个或者多个线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级的时候,唤醒优先级最高的线程,否则随机唤醒)

dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
方法作用: 等待信号量,例如
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
参数: dispatch_semaphore_t对象和 超时时间类型DISPATCH_TIME_FOREVERDISPATCH_TIME_NOW。目前一般都是选择DISPATCH_TIME_FOREVER
DISPATCH_TIME_FOREVERDISPATCH_TIME_NOW具体区别如下:

  • DISPATCH_TIME_FOREVER 超时时间为永远,表示会一直等待信号量为正数,才会继续运行
  • DISPATCH_TIME_NOW 超时时间为0,表示忽略信号量,直接运行。

3、如何使用信号量?

下面我们看下如下代码,分别设置信号量semValue0,1,23,4时的不同信号量初始值的打印结果

- (void)semaphoreTestMethodWithSemValue:(long)semValue{
    NSLog(@"\n\n\n\n");
    NSLog(@"semaphoreTestMethod 总任务开启");
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(semValue);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //任务1
    dispatch_async(quene, ^{
        NSLog(@"任务一开始");
        NSInteger currentSem = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务一 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"任务一 任务Block回掉完成 %@",status);
        }];
        
        NSLog(@"完成任务一 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(quene, ^{
        NSLog(@"任务二开始");
        NSInteger currentSem = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务二 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"任务二 任务Block回掉完成 %@",status);
        }];
        NSLog(@"完成任务二 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(quene, ^{
        NSLog(@"任务三开始");
        NSInteger currentSem = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务三 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"任务三 任务Block回掉完成 %@",status);
        }];
        NSLog(@"完成任务三 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    
    NSLog(@"semaphoreTestMethod 总任务关闭");
}

semValue = 0 打印结果

2019-04-03 17:15:32.008373+0800 GCDDemo[4793:333586] 
2019-04-03 17:15:32.008519+0800 GCDDemo[4793:333586] semaphoreTestMethod 总任务开启
2019-04-03 17:15:32.008647+0800 GCDDemo[4793:333586] semaphoreTestMethod 总任务关闭
2019-04-03 17:15:32.008695+0800 GCDDemo[4793:333639] 任务一开始
2019-04-03 17:15:32.008707+0800 GCDDemo[4793:333637] 任务二开始
2019-04-03 17:15:32.008750+0800 GCDDemo[4793:333811] 任务三开始
  • 这里设置的初始信号量为0时,我们的任务全部都被线程堵死,不在执行每个任务,因为没有任何资源执行任务。

semValue = 1 打印结果

2019-04-03 17:22:05.810571+0800 GCDDemo[4793:333586] semaphoreTestMethod 总任务开启
2019-04-03 17:22:05.810803+0800 GCDDemo[4793:333586] semaphoreTestMethod 总任务关闭
2019-04-03 17:22:05.810834+0800 GCDDemo[4793:333812] 任务一开始
2019-04-03 17:22:05.810948+0800 GCDDemo[4793:340419] 任务二开始
2019-04-03 17:22:05.810978+0800 GCDDemo[4793:340420] 任务三开始
2019-04-03 17:22:05.811148+0800 GCDDemo[4793:333812] 任务一 currentSem = 0:当前线程:<NSThread: 0x600002e8c080>{number = 3, name = (null)}
2019-04-03 17:22:05.811356+0800 GCDDemo[4793:333812] 完成任务一 currentSem = 0:当前线程:<NSThread: 0x600002e8c080>{number = 3, name = (null)}
2019-04-03 17:22:05.811557+0800 GCDDemo[4793:340419] 任务二 currentSem = 0:当前线程:<NSThread: 0x600002e8c480>{number = 5, name = (null)}
2019-04-03 17:22:05.811721+0800 GCDDemo[4793:340419] 完成任务二 currentSem = 0:当前线程:<NSThread: 0x600002e8c480>{number = 5, name = (null)}
2019-04-03 17:22:05.812068+0800 GCDDemo[4793:340420] 任务三 currentSem = 0:当前线程:<NSThread: 0x600002ee43c0>{number = 6, name = (null)}
2019-04-03 17:22:05.812324+0800 GCDDemo[4793:340420] 完成任务三 currentSem = 0:当前线程:<NSThread: 0x600002ee43c0>{number = 6, name = (null)}
2019-04-03 17:22:07.815793+0800 GCDDemo[4793:333586] 任务一 任务Block回掉完成 当前线程是:<NSThread: 0x600002e8c500>{number = 4, name = (null)} 随机等待:2秒
2019-04-03 17:22:07.816064+0800 GCDDemo[4793:333586] 任务三 任务Block回掉完成 当前线程是:<NSThread: 0x600002e8c480>{number = 5, name = (null)} 随机等待:2秒
2019-04-03 17:22:10.815416+0800 GCDDemo[4793:333586] 任务二 任务Block回掉完成 当前线程是:<NSThread: 0x600002e8c080>{number = 3, name = (null)} 随机等待:5秒
  • 这里设置的初始信号量为1时,任务会执行下去,但是每次只执行1条任务
  • 只有是在 dispatch_semaphore_wait()函数执行后返回值0的情况下,才会执行后面的回掉任务。
  • 无法控制block回掉中的耗时任务

semValue = 2 打印结果

2019-04-03 17:27:24.062999+0800 GCDDemo[4793:333586] semaphoreTestMethod 总任务开启
2019-04-03 17:27:24.063133+0800 GCDDemo[4793:333586] semaphoreTestMethod 总任务关闭
2019-04-03 17:27:24.063176+0800 GCDDemo[4793:333812] 任务一开始
2019-04-03 17:27:24.063244+0800 GCDDemo[4793:345396] 任务二开始
2019-04-03 17:27:24.063274+0800 GCDDemo[4793:345397] 任务三开始
2019-04-03 17:27:24.063346+0800 GCDDemo[4793:333812] 任务一 currentSem = 0:当前线程:<NSThread: 0x600002e8c080>{number = 3, name = (null)}
2019-04-03 17:27:24.063426+0800 GCDDemo[4793:345396] 任务二 currentSem = 0:当前线程:<NSThread: 0x600002e04580>{number = 7, name = (null)}
2019-04-03 17:27:24.063519+0800 GCDDemo[4793:333812] 完成任务一 currentSem = 0:当前线程:<NSThread: 0x600002e8c080>{number = 3, name = (null)}
2019-04-03 17:27:24.063587+0800 GCDDemo[4793:345396] 完成任务二 currentSem = 0:当前线程:<NSThread: 0x600002e04580>{number = 7, name = (null)}
2019-04-03 17:27:24.063746+0800 GCDDemo[4793:345397] 任务三 currentSem = 0:当前线程:<NSThread: 0x600002e04500>{number = 10, name = (null)}
2019-04-03 17:27:24.064110+0800 GCDDemo[4793:345397] 完成任务三 currentSem = 0:当前线程:<NSThread: 0x600002e04500>{number = 10, name = (null)}
2019-04-03 17:27:25.065614+0800 GCDDemo[4793:333586] 任务三 任务Block回掉完成 当前线程是:<NSThread: 0x600002e04580>{number = 7, name = (null)} 随机等待:1秒
2019-04-03 17:27:28.067425+0800 GCDDemo[4793:333586] 任务一 任务Block回掉完成 当前线程是:<NSThread: 0x600002e8c000>{number = 8, name = (null)} 随机等待:4秒
2019-04-03 17:27:28.067670+0800 GCDDemo[4793:333586] 任务二 任务Block回掉完成 当前线程是:<NSThread: 0x600002e8a400>{number = 9, name = (null)} 随机等待:4秒
  • 这里设置的初始信号量为2时,任务会执行下去,但是每次只执行2条任务
  • 只有是在 dispatch_semaphore_wait()函数执行后返回值0的情况下,才会执行后面的回掉任务。
  • 无法控制block回掉中的耗时任务

semValue = 3semValue = 4semValue = 2基本类似


通过以上的打印结果,我们可以知道:

  • 比如我们预计使用n个资源来处理发起的所有的排队任务,这里我们就需要创建一个初始可以使用信号总量n的信号量。
    dispatch_semaphore_t sem = dispatch_semaphore_create(n);
  • 在并发任务发起之后,我们会通过函数
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    判断当前可用的信号量值,且进入线程阻塞状态。
    当该函数的返回值=0时,那么该函数后面的任务一直会进入线程阻塞状态,一直处于等待状态
    当该函数的返回值>0时,线程阻塞解除,执行该函数后面的方法。
  • 当函数dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)的返回值>0时,线程阻塞取消,执行后面任务的同时,当前可用信号量会-1。
  • 在执行任务完成以后我们会告知系统,我们的任务执行完成了,此时会通过函数
    dispatch_semaphore_signal(semaphore);
    告知系统任务执行完毕,释放了一个信号量,当前可用信号量会+1,同时通知其它dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);线程阻塞状态的函数,判断当前返回的信号量值如此循环,直到任务全部完成。

3、信号量+并发Block回掉

如果我们将信号量结合到回掉中会是如何呢?

#pragma mark - 信号量 + 回掉
- (void)semaphoreBlockTestMethodWithSemValue:(long)semValue{
    NSLog(@"\n\n\n\n");
    NSLog(@"semaphoreTestMethod 总任务开启");
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(semValue);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //任务1
    dispatch_async(quene, ^{
        NSLog(@"任务一开始");
        NSInteger currentSem = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务一 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"任务一 任务Block回掉完成 %@",status);
            dispatch_semaphore_signal(semaphore);
        }];
        
        NSLog(@"完成任务一 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
    });
    //任务2
    dispatch_async(quene, ^{
        NSLog(@"任务二开始");
        NSInteger currentSem = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务二 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"任务二 任务Block回掉完成 %@",status);
            dispatch_semaphore_signal(semaphore);
        }];
        NSLog(@"完成任务二 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
    });
    //任务3
    dispatch_async(quene, ^{
        NSLog(@"任务三开始");
        NSInteger currentSem = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"任务三 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"任务三 任务Block回掉完成 %@",status);
            dispatch_semaphore_signal(semaphore);
        }];
        NSLog(@"完成任务三 currentSem = %ld:当前线程:%@",currentSem ,[NSThread currentThread]);
    });
    
    NSLog(@"semaphoreTestMethod 总任务关闭");
}

比如我们设置信号量的初始值为2,如下是打印结果

2019-04-03 18:09:59.097874+0800 GCDDemo[5095:376935] semaphoreTestMethod 总任务开启
2019-04-03 18:09:59.098011+0800 GCDDemo[5095:376935] semaphoreTestMethod 总任务关闭
2019-04-03 18:09:59.098036+0800 GCDDemo[5095:377194] 任务一开始
2019-04-03 18:09:59.098109+0800 GCDDemo[5095:390359] 任务二开始
2019-04-03 18:09:59.098139+0800 GCDDemo[5095:390360] 任务三开始
2019-04-03 18:09:59.098184+0800 GCDDemo[5095:377194] 任务一 currentSem = 0:当前线程:<NSThread: 0x6000025bad00>{number = 6, name = (null)}
2019-04-03 18:09:59.098282+0800 GCDDemo[5095:390359] 任务二 currentSem = 0:当前线程:<NSThread: 0x600002560240>{number = 7, name = (null)}
2019-04-03 18:09:59.098359+0800 GCDDemo[5095:377194] 完成任务一 currentSem = 0:当前线程:<NSThread: 0x6000025bad00>{number = 6, name = (null)}
2019-04-03 18:09:59.098444+0800 GCDDemo[5095:390359] 完成任务二 currentSem = 0:当前线程:<NSThread: 0x600002560240>{number = 7, name = (null)}
2019-04-03 18:10:01.100229+0800 GCDDemo[5095:376935] 任务一 任务Block回掉完成 当前线程是:<NSThread: 0x600002560680>{number = 8, name = (null)} 随机等待:2秒
2019-04-03 18:10:01.100646+0800 GCDDemo[5095:390360] 任务三 currentSem = 0:当前线程:<NSThread: 0x600002598880>{number = 10, name = (null)}
2019-04-03 18:10:01.100859+0800 GCDDemo[5095:390360] 完成任务三 currentSem = 0:当前线程:<NSThread: 0x600002598880>{number = 10, name = (null)}
2019-04-03 18:10:02.101269+0800 GCDDemo[5095:376935] 任务二 任务Block回掉完成 当前线程是:<NSThread: 0x60000257ce40>{number = 9, name = (null)} 随机等待:3秒
2019-04-03 18:10:06.103983+0800 GCDDemo[5095:376935] 任务三 任务Block回掉完成 当前线程是:<NSThread: 0x600002560680>{number = 8, name = (null)} 随机等待:5秒

  • 我们总任务有三个,设置的出事信号资源为2,当进入到并发任务后,由于只有2个可用信号资源,所以只有任务一任务二执行了回掉任务,其中任务三的回掉任务进入线程阻塞的等待中。
  • 任务一的延时回掉总共耗时2秒完成,完成后执行任务一block回掉,并通过dispatch_semaphore_signal(semaphore)告知释放了当前占用的1个信号量。
  • 剩余的任务三通过dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);延时等待的过程判断当前信号量已经>0了,判断执行任务的回掉函数任务。
  • 最终任务结束时间为任务一+任务三的耗时时间只和 与任务二的耗时时间对比,所以最终的耗时时间是7秒。
  • 通过以上可以知道,如果将dispatch_semaphore_signal(semaphore)函数放入模拟网络请求的回掉中执行,可以控制在执行block回掉之后,通知线程阻塞等待状态的信号量,是否当前有资源处理剩余的任务。

4、实现异步多线程并发任务的同步操作

#pragma mark - 实现异步多线程并发任务的同步操作
- (void)semaphoreAsyncGlobalTask{
    NSLog(@"\n\n\n\n");
    NSLog(@"semaphoreAsyncGlobalTask 总任务开启");
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"任务执行 -> 开始线程:%@",[NSThread currentThread]);      // 打印当前线程
    dispatch_async(quene, ^{
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"任务执行 一  -> 结束 线程:%@",[NSThread currentThread]);      // 打印当前线程
        dispatch_semaphore_signal(semaphore);
    });
    NSLog(@"任务执行 二 -> 结束  线程:%@",[NSThread currentThread]);      // 打印当前线程
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"任务执行 三 -> 结束  线程:%@",[NSThread currentThread]);      // 打印当前线程
    NSLog(@"semaphoreAsyncGlobalTask 总任务关闭");
}

打印结果

2019-04-04 11:47:12.788469+0800 GCDDemo[1713:126301] 任务执行 -> 开始线程:<NSThread: 0x600002c01440>{number = 1, name = main}
2019-04-04 11:47:12.788753+0800 GCDDemo[1713:126301] 任务执行 二 -> 结束  线程:<NSThread: 0x600002c01440>{number = 1, name = main}
2019-04-04 11:47:14.790166+0800 GCDDemo[1713:126762] 任务执行 一  -> 结束 线程:<NSThread: 0x600002c906c0>{number = 3, name = (null)}
2019-04-04 11:47:14.790478+0800 GCDDemo[1713:126301] 任务执行 三 -> 结束  线程:<NSThread: 0x600002c01440>{number = 1, name = main}
2019-04-04 11:47:14.790608+0800 GCDDemo[1713:126301] semaphoreAsyncGlobalTask 总任务关闭
  • 任务三实现了在任务一之后的同步执行
  • 任务二则没有实现同步执行,而是异步执行了。
  • 所以在实行多线程同步的执行任务的方式 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

我们再来看下面一段代码

#pragma mark - 实现异步多线程并发带Block回掉任务的同步操作
- (void)semaphoreAsyncGlobalBlockTask{
    NSLog(@"\n\n\n\n");
    NSLog(@"semaphoreAsyncGlobalBlockTask 总任务开启");
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"任务执行 -> 开始线程:%@",[NSThread currentThread]);      // 打印当前线程
    dispatch_async(quene, ^{
        NSLog(@"Block任务执行 -> 开始线程:%@",[NSThread currentThread]);      // 打印当前线程
        [self netWorkingComletionHandler:^(NSString *status) {
            NSLog(@"Block任务执行 一  -> 结束 线程:%@",[NSThread currentThread]);      // 打印当前线程
            dispatch_semaphore_signal(semaphore);
        }];
    });
    NSLog(@"任务执行 二 -> 结束  线程:%@",[NSThread currentThread]);      // 打印当前线程
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"任务执行 三 -> 结束  线程:%@",[NSThread currentThread]);      // 打印当前线程
    NSLog(@"semaphoreAsyncGlobalBlockTask 总任务关闭");
}

再看看其打印结果

2019-04-08 09:05:26.758092+0800 GCDDemo[1372:29281] 
2019-04-08 09:05:26.758280+0800 GCDDemo[1372:29281] semaphoreAsyncGlobalBlockTask 总任务开启
2019-04-08 09:05:26.758490+0800 GCDDemo[1372:29281] 任务执行 -> 开始线程:<NSThread: 0x600002abe940>{number = 1, name = main}
2019-04-08 09:05:26.758779+0800 GCDDemo[1372:29281] 任务执行 二 -> 结束  线程:<NSThread: 0x600002abe940>{number = 1, name = main}
2019-04-08 09:05:26.758780+0800 GCDDemo[1372:29342] Block任务执行 -> 开始线程:<NSThread: 0x600002aca280>{number = 3, name = (null)}
  • 我们发现此时dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);一直处于等待状态。而模拟的网络请求方法没有执行block 回掉任务。
  • 方法netWorkingComletionHandler中再次使用了dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{}方法,在执行该线程方法的时候,出现了线程阻塞。
  • 所以在出现以上使用情况的时候,超出了信号量的使用范围。
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容

  • 唯读书与走路不能辜负。
    路上的舞者阅读 230评论 0 0
  • 文/向哲洪涛 夕阳下的一池碧水,在轻纱般的缠绕下,朦朦胧胧,缥缈而神秘,那呢喃的晚风,犹如我轻快的歌吟,此时,我多...
    向哲洪涛阅读 970评论 2 19
  • “喂,已经出宫了,你可以放开了。”这手抓得死紧,可是疼死离雪了。 祁暗停下脚步,回头看她“我没有名字的吗?” “什...
    垣歌阅读 203评论 0 4
  • 在一回首间,才忽然发现,原来,我一生的种种努力,不过只为了周遭的人对我满意而已。为了搏得他人的称许与微笑,我战战兢...
    Myunt丶阅读 174评论 3 3
  • 暴灭宫殿。 通往地下的甬道处,都拿一脸阴沉的站立着,他手上有一个土黄色的薄衣,这颜色和人族龙晶的颜色竟然是一样的。...
    虎七阅读 630评论 3 2