iOS 线程的解析

Pthread

1.一套通用的多线程API
2.适用于Unix、Linux、Windows 的系统
3.跨平台、可移植
4.使用难度大
5.c语言
6.几乎不用
7.线程的生命周期:由程序员进行管理

NSThread

1.使用更加面向对象
2.简单易用,可直接操作线程对象
3.使用语言:OC语言
4.使用频率:偶尔使用
5.线程生命周期:由程序员进行管理

GCD

1.旨在替代NSThread等线程技术
2.充分利用设备的多核(自动)
3.使用语言:C语言
4.使用频率:经常使用
5.线程生命周期:自动管理

NSOperation

1.基于GCD(底层是GCD)
2.比GCD多了一些更简单实用的功能
3.使用更加面向对象
4.使用语言:OC语言
5.使用频率:经常使用
6.线程生命周期:自动管理

代码实现

1.NSThread

1)动态实例化

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl];
thread.threadPriority = 1;// 设置线程的优先级(0.0 - 1.0,1.0最高级)
[thread start];

2)静态方法

[NSThread detachNewThreadSelector:@selector(loadImageView:) toTarget:self withObject:imageViewUrl];

3)隐式实例化

 [self performSelectorInBackground:@selector(loadImageView:) withObject:imageViewUrl];
//动态创建线程
-(void)dynamicCreateThread{
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl];
    thread.threadPriority = 1;// 设置线程的优先级(0.0 - 1.0,1.0最高级)
    [thread start];
}

//静态创建线程
-(void)staticCreateThread{
    [NSThread detachNewThreadSelector:@selector(loadImageSource:) toTarget:self withObject:imgUrl];
}

//隐式创建线程
-(void)implicitCreateThread{
    [self performSelectorInBackground:@selector(loadImageSource:) withObject:imgUrl];
}

-(void)loadImageSource:(NSString *)url{
    NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
    UIImage *image = [UIImage imageWithData:imgData];
    if (imgData!=nil) {
        [self performSelectorOnMainThread:@selector(refreshImageView:) withObject:image waitUntilDone:YES];
    }else{
        NSLog(@"there no image data");
    }
}

-(void)refreshImageView:(UIImage *)image{
    [self.imageView setImage:image];
}

4)扩展知识

1.获取当前线程

NSThread *current = [NSThread currentThread];

2.获取主线程

NSThread *main = [NSThread mainThread];

3.暂停当前线程

[NSThread sleepForTimeInterval:2];//睡眠时间2秒钟
[NSThread sleepUntilDate:(nonnull NSDate *)]//睡眠到某一时刻

4.线程之间通信

//在指定的线程上执行
[self performSelector:@selector(loadImageView:) onThread:thread withObject:nil waitUntilDone: YES];
//在主线程上执行
 [self performSelectorOnMainThread:@selector(refreshImageView:) withObject:image waitUntilDone:YES];
//在当前线程上执行
[self performSelector:@selector(run) withObject:nil];

2.NSOperation

主要的实现方式:结合NSOperation和NSOperationQueue实现多线程编程。
实例化NSOperation的子类,绑定执行的操作。
创建NSOperationQueue队列,将NSOperation实例添加进来。
系统会自动将NSOperationQueue队列中检测取出和执行NSOperation的操作。

NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl];
//[invocationOperation start];//直接会在当前线程主线程执行
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:invocationOperation];
    
    NSBlockOperation *blockOperationB=[NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"当前线程--1--%@",[NSThread currentThread]);
    }];
    [queue addOperation:blockOperationB];
    
    NSBlockOperation *blockOperationC=[NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"当前线程--2--%@",[NSThread currentThread]);
    }];
    [queue addOperation:blockOperationC];

异步操作

3.GCD

//GCD延迟执行函数
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(sceonds * NSEC_PER_SEC)), dispatch_get_main_queue(), block);

自定义队列

//GCD异步队列
  dispatch_queue_t currentQueue = dispatch_queue_create("com.comcurrent", DISPATCH_QUEUE_CONCURRENT);

//GCD串行队列
  dispatch_queue_t serialQueue = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);

系统标准队列

//全局队列,一个并行的队列
dispatch_get_global_queue
//主队列,主线程中的唯一队列,一个串行队列
dispatch_get_main_queue

同步异步线程

//同步线程
dispatch_sync(..., ^(block))
//异步线程
dispatch_async(..., ^(block))

*自定义队列的优先级:可以通过dipatch_queue_attr_make_with_qos_class或dispatch_set_target_queue方法设置队列的优先级

//dipatch_queue_attr_make_with_qos_class
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, -1);
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.qosqueue", attr);

//dispatch_set_target_queue
dispatch_queue_t queue = dispatch_queue_create("com.starming.gcddemo.settargetqueue",NULL); //需要设置优先级的queue
dispatch_queue_t referQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
dispatch_set_target_queue(queue, referQueue); //设置queue和referQueue的优先级一样

*dispatch_set_target_queue:可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行,如下

dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t firstQueue = dispatch_queue_create("com.starming.gcddemo.firstqueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t secondQueue = dispatch_queue_create("com.starming.gcddemo.secondqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_set_target_queue(firstQueue, serialQueue);
dispatch_set_target_queue(secondQueue, serialQueue);

dispatch_async(firstQueue, ^{
    NSLog(@"1");
    [NSThread sleepForTimeInterval:3.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"2");
    [NSThread sleepForTimeInterval:2.f];
});
dispatch_async(secondQueue, ^{
    NSLog(@"3");
    [NSThread sleepForTimeInterval:1.f];
});

dispatch_once_t要是全局或static变量,保证dispatch_once_t只有一份实例
程序运行起来,保证只执行一次代码。

这里可以用来创建单列,但是会有问题,如果这个单例一旦被指为nil, 就不会被创建了,因为这部分代码只能执行一次。

+ (UIColor *)boringColor;
{
     static UIColor *color;
     //只运行一次
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
          color = [UIColor colorWithRed:0.380f green:0.376f blue:0.376f alpha:1.000f];
     });
     return color;
}

可以避免界面会被一些耗时的操作卡死,比如读取网络数据,大数据IO,还有大量数据的数据库读写,这时需要在另一个线程中处理,然后通知主线程更新界面,GCD使用起来比NSThread和NSOperation方法要简单方便。

//代码框架
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     // 耗时的操作
     dispatch_async(dispatch_get_main_queue(), ^{
          // 更新界面
     });
});

Block组合Dispatch_groups

dispatch groups是专门用来监视多个异步任务。dispatch_group_t实例用来追踪不同队列中的不同任务。

当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。

- (void)dispatchGroupWaitDemo {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    //在group中添加队列的block
    dispatch_group_async(group, concurrentQueue, ^{
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"1");
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"2");
    });
// 阻塞当前线程,以上任务执行完,才会执行 后面的代码,
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"go on");
}

//dispatch_group_notify
- (void)dispatchGroupNotifyDemo {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"1");
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"2");
    });
//异步执行,不会阻塞当前线程,
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"end");
    });
    NSLog(@"can continue");
}

dispatch_group_async等价于dispatch_group_enter() 和 dispatch_group_leave()的组合。
dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的

*dispatch_block_wait:可以根据dispatch block来设置等待时间,参数DISPATCH_TIME_FOREVER会一直等待block结束

- (void)dispatchBlockWaitDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"star");
        [NSThread sleepForTimeInterval:5.f];
        NSLog(@"end");
    });
    dispatch_async(serialQueue, block);
    //设置DISPATCH_TIME_FOREVER会一直等到前面任务都完成
    dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
    NSLog(@"ok, now can go on");
}

*dispatch_block_cancel:iOS8后GCD支持对dispatch block的取消

- (void)dispatchBlockCancelDemo {
    dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
    dispatch_block_t firstBlock = dispatch_block_create(0, ^{
        NSLog(@"first block start");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"first block end");
    });
    dispatch_block_t secondBlock = dispatch_block_create(0, ^{
        NSLog(@"second block run");
    });
    dispatch_async(serialQueue, firstBlock);
    dispatch_async(serialQueue, secondBlock);
    //取消secondBlock
    dispatch_block_cancel(secondBlock);
}

GCD 信号量控制并发 (dispatch_semaphore)

当我们在处理一系列线程的时候,当数量达到一定量,在以前我们可能会选择使用NSOperationQueue来处理并发控制,但如何在GCD中快速的控制并发呢?答案就是dispatch_semaphore
在GCD中有三个函数是semaphore的操作,分别是:
  dispatch_semaphore_create   创建一个semaphore
  dispatch_semaphore_signal   发送一个信号
  dispatch_semaphore_wait    等待信号
  简单的介绍一下这三个函数,第一个函数有一个整形的参数,我们可以理解为信号的总量,dispatch_semaphore_signal是发送一个信号,自然会让信号总量加1,dispatch_semaphore_wait等待信号,当信号总量少于0的时候就会一直等待,否则就可以正常的执行,并让信号总量-1,根据这样的原理,我们便可以快速的创建一个并发控制来同步任务和有限资源访问控制。

dispatch_group_t group = dispatch_group_create(); 
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(10); 
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
    for (int i = 0; i < 100; i++) 
    { 
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
        dispatch_group_async(group, queue, ^{ 
            NSLog(@"%i",i); 
            sleep(2); 
            dispatch_semaphore_signal(semaphore); 
        }); 
    } 
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
    dispatch_release(group); 
    dispatch_release(semaphore); 

简单的介绍一下这一段代码,创建了一个初使值为10的semaphore,每一次for循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了10个线程之后,for循环就会阻塞,等待有线程结束之后会增加一个信号才继续执行,如此就形成了对并发的控制,如上就是一个并发数为10的一个线程队列。

关于信号量,一般可以用停车来比喻。

停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。

信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal

就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),

调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;

当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主

没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,

所以就一直等下去。

通过GCD中的dispatch_barrier_(a)sync加强对sync中所谓等待的理解

对于dispatch_barrier_async可能有的朋友没用过,不知道它是干嘛的,简单地介绍一下下,知道的朋友可以跳过此段。
假设我们原先有6个任务要执行,我们现在要插入一个任务0,这个任务0要在1、2、3都并发执行完了之后才能执行,而4、5、6号任务要在这个任务0结束后才允许并发。

对于这样一种需求,很多朋友的第一反应就是用个group就解决了。确实如此,但是系统提供了一种更加简单地方法,那就是dispatch_barrier_async,我们只要按照前面所述的顺序将任务分配到队列就OK,剩下的都不用管了。dispatch_barrier_async的参数跟dispatch_async一模一样的。

下面开始讲正题
总结前面所说,dispatch_barrier_async是会等待前面提到的任务0结束的,注意这里是async。说到等待大家必然会想到dispatch_sync,dispatch_sync的任务是串行的,会等待任务结束程序再继续往下走。那dispatch_barrier是否存在一个sync的方法呢?存在……那么问题来了……那dispatch_barrier_async和dispatch_barrier_sync的区别在哪呢?如果没有区别的话苹果何必搞出2个函数呢,区别必然是有的。

先贴上代码,代码非常简单,就是按照之前提的需求写的。

- (void)dispatchBarrier {
    dispatch_queue_t queue=dispatch_queue_create("com.com.com.com", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        
        NSLog(@"test1");
        
    });
    dispatch_async(queue, ^{
        
        NSLog(@"test2");
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"test3");
        
    });
    
    dispatch_barrier_sync(queue, ^{
        
        for(int i=0;i<500000000;i++){
            
            if(i==5000){
                
                NSLog(@"point1");
            }else if (6000==i){
                NSLog(@"point2");
            }else if (7000==i){
                NSLog(@"point3");
            }
        }
        NSLog(@"barrier");
    });
    
    NSLog(@"aaaa");
    
    dispatch_async(queue, ^{
        
        NSLog(@"test4");
    });
    
    NSLog(@"bbbb");
    
    dispatch_async(queue, ^{
        
        NSLog(@"test5");
    });
    dispatch_async(queue, ^{
        
        NSLog(@"test6");
    });

}

以上代码写的是dispatch_barrier_sync,因为我们需要先看看我们熟悉的等待——sync是什么效果,直接跑起来

2017-11-14 18:08:15.418268+0800 GCDBarrierDemo[55236:1711559] test1
2017-11-14 18:08:15.418268+0800 GCDBarrierDemo[55236:1711926] test2
2017-11-14 18:08:15.418306+0800 GCDBarrierDemo[55236:1711929] test3
2017-11-14 18:08:15.418925+0800 GCDBarrierDemo[55236:1711362] point1
2017-11-14 18:08:15.419036+0800 GCDBarrierDemo[55236:1711362] point2
2017-11-14 18:08:15.419125+0800 GCDBarrierDemo[55236:1711362] point3
2017-11-14 18:08:17.665760+0800 GCDBarrierDemo[55236:1711362] barrier
2017-11-14 18:08:17.665907+0800 GCDBarrierDemo[55236:1711362] aaaa
2017-11-14 18:08:17.665993+0800 GCDBarrierDemo[55236:1711362] bbbb
2017-11-14 18:08:17.666004+0800 GCDBarrierDemo[55236:1711929] test4
2017-11-14 18:08:17.666093+0800 GCDBarrierDemo[55236:1711926] test5
2017-11-14 18:08:17.666104+0800 GCDBarrierDemo[55236:1711929] test6

可以看到,确实是1、2、3号任务并发执行完了,然后再执行的我们的0号任务,再并发执行的4、5、6号任务,当然,point3和barrier之间是有明显停顿的,截图无法表现。对于这个输出,应该是意料之中的。截下来,我们来看看async的效果

代码进行一点点修改,dispatch_barrier_sync改成dispatch_barrier_async。我这里先把aaa、bbb的输出隐藏掉。改完代码可以直接跑起来,我们一起看看结果

2017-11-14 18:09:10.323530+0800 GCDBarrierDemo[55324:1715821] test1
2017-11-14 18:09:10.323560+0800 GCDBarrierDemo[55324:1715822] test2
2017-11-14 18:09:10.323573+0800 GCDBarrierDemo[55324:1715823] test3
2017-11-14 18:09:10.328436+0800 GCDBarrierDemo[55324:1715823] point1
2017-11-14 18:09:10.330174+0800 GCDBarrierDemo[55324:1715823] point2
2017-11-14 18:09:10.330643+0800 GCDBarrierDemo[55324:1715823] point3
2017-11-14 18:09:12.275495+0800 GCDBarrierDemo[55324:1715823] barrier
2017-11-14 18:09:12.275657+0800 GCDBarrierDemo[55324:1715823] test4
2017-11-14 18:09:12.275657+0800 GCDBarrierDemo[55324:1715822] test5
2017-11-14 18:09:12.275662+0800 GCDBarrierDemo[55324:1715821] test6

好像除了aaa、bbb之外其它的都跟上面sync的情况一模一样(当然,并发的顺序无法控制),而且point3和barrier之间同样有明显停顿,看来,这个dispatch_barrier_async确实会等待它的任务0执行完。

既然这样那dispatch_barrier_async和dispatch_barrier_sync究竟有什么区别呢?我们把aaa、bbb的输出打开看看就知道了。

2017-11-14 18:09:44.828321+0800 GCDBarrierDemo[55370:1718558] aaaa
2017-11-14 18:09:44.828321+0800 GCDBarrierDemo[55370:1718916] test3
2017-11-14 18:09:44.828321+0800 GCDBarrierDemo[55370:1718917] test1
2017-11-14 18:09:44.828321+0800 GCDBarrierDemo[55370:1718925] test2
2017-11-14 18:09:44.828767+0800 GCDBarrierDemo[55370:1718558] bbbb
2017-11-14 18:09:44.828877+0800 GCDBarrierDemo[55370:1718925] point1
2017-11-14 18:09:44.831279+0800 GCDBarrierDemo[55370:1718925] point2
2017-11-14 18:09:44.831811+0800 GCDBarrierDemo[55370:1718925] point3
2017-11-14 18:09:46.660403+0800 GCDBarrierDemo[55370:1718925] barrier
2017-11-14 18:09:46.660592+0800 GCDBarrierDemo[55370:1718916] test5
2017-11-14 18:09:46.660592+0800 GCDBarrierDemo[55370:1718925] test4
2017-11-14 18:09:46.660611+0800 GCDBarrierDemo[55370:1718917] test6

区别很明显,跟sync的情况相比,aaa、bbb的输出位置完全不同,async的时候aaa的输出在任务0结束之前,sync的aaa输出在任务0结束之后。

好了,说到这应该差不多能想通了,我们开始总结
dispatch_barrier_sync和dispatch_barrier_async的共同点:
1、都会等待在它前面插入队列的任务(1、2、3)先执行完
2、都会等待他们自己的任务(0)执行完再执行后面的任务(4、5、6)

dispatch_barrier_sync和dispatch_barrier_async的不共同点:
在将任务插入到queue的时候,dispatch_barrier_sync需要等待自己的任务(0)结束之后才会继续程序,然后插入被写在它后面的任务(4、5、6),然后执行后面的任务
而dispatch_barrier_async将自己的任务(0)插入到queue之后,不会等待自己的任务结束,它会继续把后面的任务(4、5、6)插入到queue

所以,dispatch_barrier_async的不等待(异步)特性体现在将任务插入队列的过程,它的等待特性体现在任务真正执行的过程。

总结:就是asyn不阻塞当前线程而已。。。barrier只是阻塞同队列中后面的操作而已

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

推荐阅读更多精彩内容

  • 锁是一种同步机制,用于多线程环境中对资源访问的限制iOS中常见锁的性能对比图(摘自:ibireme): iOS锁的...
    LiLS阅读 1,457评论 0 6
  • 本篇博客共分以下几个模块来介绍GCD的相关内容: 多线程相关概念 多线程编程技术的优缺点比较? GCD中的三种队列...
    有梦想的老伯伯阅读 995评论 0 4
  • iOS中GCD的使用小结 作者dullgrass 2015.11.20 09:41*字数 4996阅读 20199...
    DanDanC阅读 768评论 0 0
  • 文|艾溪 01. 朋友跟我讲了一件她最近遇到的烦心事。 事情的起因她近期忙着考研,自己的电脑就不怎么用,她的室友因...
    吴立言Aice阅读 4,587评论 79 102
  • 今年的这个冬天 整天都是灰蒙蒙的样子,无精打采 我把手洗干净,将污浊的窗擦了又擦 太阳被隔绝在另一个世界,星星也只...
    利君理疗阅读 149评论 0 0