2023/03/06 线程

进程:正在进行中的程序被称为进程,负责程序运行的内存分配
每一个进程都有自己独立的虚拟内存空间

线程:线程是进程中一个独立的执行路径(控制单元)
一个进程中至少包含一条线程,即主线程

操作:任务/
同步操作(sync) :一个接着一个,前一个没有执行完,后面不能执行,不开线程。
会依次顺序执行,能够决定任务的执行顺序/
异步操作(async):开启多个新线程,任务同一时间可以一起执行。异步是多线程的代名词
会并发执行,无法确定任务的执行顺序

队列:装载线程任务的队形结构。
(系统以先进先出的方式调度队列中的任务执行)。
在GCD中有两种队列:串行队列和并发队列。/
并行队列:队列中的任务通常会并发执行
线程可以同时一起进行执行。实际上是CPU在多条线程之间快速的切换。(并发功能只有在异步(dispatch_async)函数下才有效)/
串行队列:队列中的任务只会顺序执行
线程只能依次有序的执行/
主队列:主队列负责在主线程上调度任务,如果在主线程上已经有任务正在执行,主队列会等到主线程空闲后再调度任务。通常是返回主线程更新UI的时候使用。dispatch_get_main_queue()/
全局并发队列:全局并发队列是就是一个并发队列,是为了让我们更方便的使用多线程。dispatch_get_global_queue(0, 0)/

// 同步执行任务
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        // 任务放在这个block里
        NSLog(@"我是同步执行的任务");
    });
    // 异步执行任务
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 任务放在这个block里
        NSLog(@"我是异步执行的任务");
    });

使用dispatch_queue_create来创建队列对象,传入两个参数,
第一个参数表示队列的唯一标识符,可为空。
第二个参数用来表示串行队列(DISPATCH_QUEUE_SERIAL)或
并发队列(DISPATCH_QUEUE_CONCURRENT)。

dispatch_queue_t serialQueue1 = dispatch_queue_create("com.YDW.Queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.YDW.Queue", DISPATCH_QUEUE_CONCURRENT);

主队列:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // 耗时操作放在这里
      dispatch_async(dispatch_get_main_queue(), ^{
          // 回到主线程进行UI操作
      });
  });

全局并发队列:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//全局并发队列的优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高优先级
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)优先级
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低优先级
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台优先级
//iOS8开始使用服务质量,现在获取全局并发队列时,可以直接传0
dispatch_get_global_queue(0, 0);
串行同步  
|123456789|    1个人 1干到9 干完
串行异步
|123          |         1个人 1干到3 干完叫
|     456     |         1个人4干到6  干完叫
|          789|         1个人7干到9  干完
并发同步
|1               |    1个人 1 干完叫
| 2              |    1个人 2 干完叫
。。。
|               9|    1个人 9 干完
并发异步  一起干
|1               |    1个人 1 干完
|2               |    1个人 2 干完
。。。
|9               |    1个人 9 干完
主队列同步
|123456789|    1个人 1干到9 干完 
还没干完1就 叫干2  会造成等待死锁
主队列异步 一起干
|1               |    1个人 1 干完
|2               |    1个人 2 干完
。。。
|9               |    1个人 9 干完
GCD线程之间的通讯
开发中需要在主线程上进行UI的相关操作,通常会把一些耗时的操作放在其他线程,比如说图片文件下载等耗时操作。

当完成了耗时操作之后,需要回到主线程进行UI的处理,这里就用到了线程之间的通讯。

当需要等待一会再执行一段代码时,就可以用到这个方法了:dispatch_after。
和只执行了一次 dispatch_once

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 5秒后异步执行
    NSLog(@"我已经等待了5秒!");
});
GCD实现代码只执行一次
使用dispatch_once能保证某段代码在程序运行过程中只被执行1次。可以用来设计单例。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"程序运行过程中我只执行了一次!");
});

GCD栅栏
当任务需要异步进行,但是这些任务需要分成两组来执行,第一组完成之后才能进行第二组的操作。这时候就用了到GCD的栅栏方法dispatch_barrier_async。

- (IBAction)barrierGCD:(id)sender {
    // 并发队列
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    // 异步执行
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"栅栏:并发异步1   %@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"栅栏:并发异步2   %@",[NSThread currentThread]);
        }
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"------------barrier------------%@", [NSThread currentThread]);
        NSLog(@"******* 并发异步执行,但是34一定在12后面 *********");
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"栅栏:并发异步3   %@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"栅栏:并发异步4   %@",[NSThread currentThread]);
        }
    });
}

GCD快速迭代
GCD有一个快速迭代的方法dispatch_apply,dispatch_apply可以同时遍历多个数字。
GCD队列组
队列组有下面几个特点:
所有的任务会并发的执行(不按序)。
所有的异步函数都添加到队列中,然后再纳入队列组的监听范围。
使用dispatch_group_notify函数,来监听上面的任务是否完成,如果完成, 就会调用这个方法。

- (void)testGroup {
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"队列组:有一个耗时操作完成!");
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"队列组:有一个耗时操作完成!");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"队列组:前面的耗时操作都完成了,回到主线程进行相关操作");
    });
}

NSOperation是基于GCD之上的更高一层封装,NSOperation需要配合NSOperationQueue来实现多线程。
NSOperation实现多线程的步骤如下:

  1. 创建任务:先将需要执行的操作封装到NSOperation对象中。
  2. 创建队列:创建NSOperationQueue。
  3. 将任务加入到队列中:将NSOperation对象添加到NSOperationQueue中
    使用:
    使用子类NSInvocationOperation
    使用子类NSBlockOperation
    定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。
    NSInvocationOperation
- (void)testNSInvocationOperation {
    // 创建NSInvocationOperation
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
    // 开始执行操作
    [invocationOperation start];
}
- (void)invocationOperation {
    NSLog(@"NSInvocationOperation包含的任务,没有加入队列========%@", [NSThread currentThread]);
}

NSBlockOperation

- (void)testNSBlockOperationExecution {
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation运用addExecutionBlock主任务========%@", [NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperation运用addExecutionBlock方法添加任务1========%@", [NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperation运用addExecutionBlock方法添加任务2========%@", [NSThread currentThread]);
    }];
    [blockOperation addExecutionBlock:^{
        NSLog(@"NSBlockOperation运用addExecutionBlock方法添加任务3========%@", [NSThread currentThread]);
    }];
    [blockOperation start];
}

定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。

/*******************"WHOperation.h"*************************/
#import @interface WHOperation : NSOperation
@end
/*******************"WHOperation.m"*************************/
#import "WHOperation.h"
@implementation WHOperation
- (void)main {
    for (int i = 0; i < 3; i++) {
        NSLog(@"NSOperation的子类WHOperation======%@",[NSThread currentThread]);
    }
}
@end
/*****************回到主控制器使用WHOperation**********************/
- (void)testWHOperation {
    WHOperation *operation = [[WHOperation alloc] init];
    [operation start];
}
//主队列
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
//非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

非主队列(其他队列)可以实现串行或并行。
队列NSOperationQueue有一个参数叫做最大并发数:maxConcurrentOperationCount。
maxConcurrentOperationCount默认为-1,直接并发执行,所以加入到‘非队列’中的任务默认就是并发,开启多线程。
当maxConcurrentOperationCount为1时,则表示不开线程,也就是串行。
当maxConcurrentOperationCount大于1时,进行并发执行。
系统对最大并发数有一个限制,所以即使程序员把maxConcurrentOperationCount设置的很大,系统也会自动调整。所以把最大并发数设置的很大是没有意义的。
任务加到队列

- (void)testOperationQueue {
    // 创建队列,默认并发
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 创建操作,NSInvocationOperation
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperationAddOperation) object:nil];
    // 创建操作,NSBlockOperation
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"addOperation把任务添加到队列======%@", [NSThread currentThread]);
        }
    }];
    [queue addOperation:invocationOperation];
    [queue addOperation:blockOperation];
}
- (void)invocationOperationAddOperation {
    NSLog(@"invocationOperation===aaddOperation把任务添加到队列====%@", [NSThread currentThread]);
}

任务加到队列 简便写法

- (void)testAddOperationWithBlock {
    // 创建队列,默认并发
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 最大并发数为1,串行
    queue.maxConcurrentOperationCount = 1;
    // 最大并发数为2,并发
//    queue.maxConcurrentOperationCount = 2;
    // 添加操作到队列
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"addOperationWithBlock把任务添加到队列======%@", [NSThread currentThread]);
        }
    }];
}
- (void)cancelAllOperations;
- (void)cancel    ;
// 暂停队列
[queue setSuspended:YES];
判断队列是否暂停
- (BOOL)isSuspended;
某一个操作(operation2)依赖于另一个操作(operation1),只有当operation1执行完毕,才能执行operation2.

NSOperation的操作依赖

- (void)testAddDependency {
    // 并发队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 操作1
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"operation1======%@", [NSThread  currentThread]);
        }
    }];
    // 操作2
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"****operation2依赖于operation1,只有当operation1执行完毕,operation2才会执行****");
        for (int i = 0; i < 3; i++) {
            NSLog(@"operation2======%@", [NSThread  currentThread]);
        }
    }];
    // 使操作2依赖于操作1
    [operation2 addDependency:operation1];
    // 把操作加入队列
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

https://blog.csdn.net/liqun3yue25/article/details/88258532
实际运用中还用到的
iOS GCD group 多请求异步 semaphore 多请求同步
1.多图上传的功能,而恰巧服务器可能只支持单张上传 重复请求多次接口

//创建一个组
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i <9; I++)
{//模仿多个网络请求
    dispatch_group_async(group,dispatch_get_global_queue(0, 0), ^{
        //异步网络请求
        
        int x = arc4random() % 5;
        //模拟网络请求快慢不确定的情况
        sleep(x);
        
        NSLog(@"group 请求成功OR请求失败 %d!",i);
    });
}
NSLog(@"group开始 网络请求!");

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    //网络请求完毕 回到主线程更新UI 或者做些其它的操作
    NSLog(@"group所有请求完毕!!!");
});

2.用到的 接口请求

//创建一个组
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i <9; I++)
{//模仿多个网络请求
    
    dispatch_group_enter(group);
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //异步网络请求
        
        int x = arc4random() % 5;
        //模拟网络请求快慢不确定的情况
        sleep(x);
        NSLog(@"group 请求成功OR请求失败 %d!",i);
        
        dispatch_group_leave(group);
    });
}
NSLog(@"group开始 网络请求!");

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    //网络请求完毕 回到主线程更新UI 或者做些其它的操作
    NSLog(@"group所有请求完毕!!!");
});

3.比如要做一个相册功能,也是多图上传,但是要有规律按顺序上传

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    /// 创建一个线程"001" 确保之后不要阻塞主线程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        /// 创建一个信号量 数值为1  信号量可以让线程阻塞等待
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
        for (int i = 0; i<9; i++) {
            /// 模仿9个请求任务
            /// 执行dispatch_semaphore_wait 信号量数值 -1.
            /// 当i为0此时的信号量数值为0, 当此时的信号量大于等于0继续执行wait函数下面的语句.
            /// 当i为1此时的信号量数值为-1, 阻塞当前线程 阻塞时长为DISPATCH_TIME_FOREVER, 不执行wait函数下面的语句.
            /// 只有等到执行i为0 的 dispatch_semaphore_signal 方法执行, 信号量数值+1 为0, 唤醒 继续执行wait函数下面的语句.
            /// 以此类推循环.
            NSLog(@"当前线程:%@",[NSThread currentThread]);
        
            /// ******这是一个网络请求开始******
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                /// 模拟异步网络请求
                int x = arc4random() % 2;
                /// 模拟网络请求快慢
                sleep(x);
                NSLog(@"执行任务代号:%d",i);
                /// 任务结束 信号量数值+1 解放线程"001" 阻塞
                dispatch_semaphore_signal(semaphore);
            });
            /// ******这是一个网络请求结束******

            /// 信号量减1 变为负数 当前线程"001" 阻塞
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        }
    });
}
dispatch_group_wait
暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。

dispatch_source 使用场景:由于 dispatch_source 不依赖于 Runloop,而是直接和底层内核交互,准确性更高,所以经常用于验证码倒计时。

  • (void)use {
    // 倒计时时间
    __block int timeout = 3;
    // 创建队列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);// 创建timerdispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, globalQueue);// 设置1s触发一次,0s的误差/- source 分派源- start 数控制计时器第一次触发的时刻。参数类型是 dispatch_time_t,这是一个opaque类型,我们不能直接操作它。我们得需要 dispatch_time 和 dispatch_walltime 函数来创建它们。另外,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。- interval 间隔时间- leeway 计时器触发的精准程度/
    dispatch_source_set_timer(timer,dispatch_walltime(NULL,0),
    1.0*NSEC_PER_SEC, 0);
    // 触发的事件
    dispatch_source_set_event_handler(timer, ^{// 倒计时结束,关闭
    if (timeout <= 0) {// 取消dispatch源
    dispatch_source_cancel(timer);
    }else{
    timeout--;
    dispatch_async(dispatch_get_main_queue(), ^{// 更新主界面的操作
    NSLog(@"倒计时 - %d", timeout);
    });
    }
    });// 开始执行dispatch源
    dispatch_resume(timer);
    }

https://www.jianshu.com/p/8416d541ef5a
https://betheme.net/news/txtlist_i192062v.html?action=onClick
https://www.jianshu.com/p/c1f4d7ef6002

    if (self.cantCommentCourse && self.objectType.intValue == 4) {
        [DJAlertController showTagWithMessage:@"购买后才可发表想法"];
        return;
    }

    self.imageUrlString = @"";
    [GBMBProgressHUD showMessage:@""];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 执行 同步并且顺序执行
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        __block NSInteger count = 0;
        __block NSMutableArray *imageMArray = imageArrM.mutableCopy;
        for (NSInteger i = 0; i < imageArrM.count; ++i) {
            UIImage *image = [UIImage imageCompressDefaultScale:imageArrM[i]];
            NSData *imageData = UIImageJPEGRepresentation(image, 0.5);
            // 文件命名
            NSDictionary *imageDictionary = @{@"file" : imageData};

            dispatch_queue_t concurrentQ = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
            dispatch_async(concurrentQ, ^{
                @weakify_self
                [DJDiscoverRecommendManager dj_GEEBOO_file_oss_imgByFile:imageDictionary bizType:@"2" successBlock:^(NSDictionary *data){
                    @strongify_self
                    if ([NSString isEmpty:self.imageUrlString]){
                        self.imageUrlString = data[@"fileName"];
                    }else{
                        self.imageUrlString = [NSString stringWithFormat:@"%@|%@",self.imageUrlString,data[@"fileName"]];
                    }
                    count ++;
                    if (count == imageMArray.count) {
                        dispatch_semaphore_signal(semaphore);
                    }
                } failureBlock:^(NSError *error){
                    LOG_ME_DEBUG(@"%@",error.domain);
                    count ++;
                    if (count == imageMArray.count) {
                        dispatch_semaphore_signal(semaphore);
                    }else{
                        [GBMBProgressHUD showError:error.domain];
                    }
                }];
            });
        }
        if (imageMArray.count == 0) {// 没有图片时,终止「阻塞当前线程」
            dispatch_semaphore_signal(semaphore);
        }

        // dispatch_semaphore_t 信号量会阻塞当前进程
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        LOG_ME_DEBUG(@"waiting。。。");

        NSMutableDictionary *params = [NSMutableDictionary dictionaryWithCapacity:0];
        [params setObject:self.objectId forKey:@"objectId"];
        [params setObject:content forKey:@"content"];
        if (self.imageUrlString.length>0) {
           [params setObject:self.imageUrlString forKey:@"images"];
        }
        [params setObject:self.objectType forKey:@"objectType"];
        @weakify_self
        [DJCommentManager dj_commentCommentAddWithParams:params Finished:^(BOOL isHttpSuccess) {
            [GBMBProgressHUD hideHUD];
            @strongify_self
            if (isHttpSuccess) {
                [self.bottomCommentView initContent];
            }
        }];
    });

    [self.tableView setHeaderFooterisHidden:YES];
    
    [GBMBProgressHUD showToView:self.view frame:FRAME(0, 0, kScreenWidth, self.view.height)];
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    @weakify_self
    [self getRequestCommendData:^(BOOL isHttpSuccess) {
        @strongify_self
        NSLog(@"评论详情,%@",@(self.tableView.height));
        dispatch_group_leave(group);
    }];
    
    dispatch_group_enter(group);
    self.tableView.sinceId = @"0";
    self.tableView.maxId = @"";
    [self getRequestData:^(BOOL isHttpSuccess) {
        @strongify_self
        NSLog(@"用户回复,%@",@(self.tableView.height));
        dispatch_group_leave(group);
    }];
    
    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            [GBMBProgressHUD hideHUDForView:self.view];
            if (self.commentModel||self.tableView.dataArray.count>0) {
                [self createRefreshTool];
                [self setTableHeaderView];
                self.bottomCommentView.hidden = NO;
                [self.bottomCommentView revplyParentReplyId:@"0" CommentId:self.commentId NickName:@""];
            }else{
                [self extractedConfigNoNetView];
            }
        });
    });
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容