iOS开发之多线程技术(NSThread、OperationQueue、GCD)

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

在前面的博客中如果用到了异步请求的话,也是用到的第三方的东西,没有正儿八经的用过iOS中多线程的东西。其实多线程的东西还是蛮重要的,如果对于之前学过操作系统的小伙伴来说,理解多线程的东西还是比较容易的,今天就做一个小的demo来详细的了解一下iOS中的多线程的东西。可能下面的东西会比较枯燥,但还是比较实用的。

多线程用的还是比较多的,废话少说了,下面的两张截图是今天我们实验的最终结果,应该是比较全的,小伙伴们由图来分析具体的功能吧:

功能说明:

1、点击同步请求图片,观察整个UI界面的变化,并点击测试按钮,红色是否会变成绿色。

2、NSThread按钮,是由NSThread方式创建线程并执行相应的操作。

3、Block操作按钮是用Block创建操作,并在操作队列中执行,下面的是Invocation操作

4、serial是GCD中的串行队列,concurrent是GCD中的并行队列

好啦,上面的咸蛋先到这儿,代码该走起啦。

一、准备阶段

1.不管使用代码写,还是storyboard或者xib等,先把上面所需的控件初始化好以便使用

2.点击测试UI按钮,改变下边label的颜色的代码如下:

1//改变lable的颜色,在红绿颜色之间进行交换2- (IBAction)tapTestButton:(id)sender {3staticinti =1;4if(i ==1) {5_testLabel.backgroundColor =[UIColor redColor];6i =0;7}8else9{10_testLabel.backgroundColor =[UIColor greenColor];11i =1;12}1314}

3.从网络上获取图片,并使用主线程显示进程调用情况

1//从wang'lu获取图片数据2-(NSData *) getImageData3{45_count ++;6intcount =_count;

//线程开始启动7NSString *str = [NSString stringWithFormat:@"%d.线程%@",count,[NSThread currentThread]];

NSLog(@"%@",str);

8NSData *data;9[NSThread sleepForTimeInterval:0.5];10data =[NSData dataWithContentsOfURL:[NSURL URLWithString:IMAGEURL]];1112NSString *str = [NSString stringWithFormat:@"%d.线程%@完毕",count,[NSThread currentThread]];13//请求数据的任务由其他线程解决,所以LogTextView的内容由主线程更新,也只有主线程才能更新UI14[self performSelectorOnMainThread:@selector(updateTextViewWithString:) withObject:str waitUntilDone:YES];15returndata;16}

4.上面的用到了主线程来调用updateTextViewWithString方法,因为只有主线程才能更新UI,updateTextViewWithString:这个方法负责把线程的执行信息显示在View上,代码如下:

1//在ViewController上显示图片请求情况2-(void)updateTextViewWithString:(NSString *)str3{4NSString *old_str = [NSString stringWithFormat:@"%@\n%@",_logTextView.text, str];56_logTextView.text =old_str;7//改变Label的颜色,便于观察8[self tapTestButton:nil];9}

5.把请求完的图片加载到ImageView上

1//更新图片2-(void) updateImageWithData:(NSData *)data3{4UIImage *image =[UIImage imageWithData:data];5[_testImage setImage:image];6}

6.加载图片的,也就是请求数据后在ImageView上显示

1//由其他线程请求数据,由主线程来更新UI2-(void)loadImageWithThreadName:(NSString *)threadName3{4[[NSThread currentThread] setName:threadName];56NSData *data =[self getImageData];7[self performSelectorOnMainThread:@selector(updateImageWithData:) withObject:data waitUntilDone:YES];8}

二、通过各种方式来

1.同步请求图片测试,请求数据和更新UI都放在主线程中顺序执行,这样在请求数据的时候UI会卡死,代码如下;

1//同步请求图片,视图阻塞的,因为主线程被占用,无法进行视图的更新2- (IBAction)tapButton:(id)sender {3NSData *data =[self getImageData];4[self updateImageWithData:data];5}

2.NSThread创建线程测试,用detachNewThreadSelector方法来创建新的线程会自动启动并执行,而不用调用start方法。代码如下:

1//NSThread2- (IBAction)tapButton2:(id)sender {3//点击一次button就创建一个新的线程来请求图片数据4for(inti =0;i <10; i ++) {5[NSThread detachNewThreadSelector:@selector(loadImageWithThreadName:) toTarget:self withObject:@"NSThread"];6}7}

3.NSInvocationOperation的使用,新建一个调用操作,然后添加到队列中执行,代码如下:

1//NSInvocationOperation2- (IBAction)tapInvocationOperation:(id)sender {345//上面的调用操作需要放到调用队列里才执行的6//创建操作队列7NSOperationQueue *operationQueue =[[NSOperationQueue alloc] init];89for(inti =0;i <10; i ++) {10//实例化一个调用操作,来执行数据请求11NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageWithThreadName:)object:@"Invocation"];12//把上面的调用操作放到操作队列里,队列会自动开启一个线程调用我们指定的方法13[operationQueue addOperation:invocationOperation];14}15}

4.block的操作,新建一个block操作,并添加到队列中执行,代码如下:

1//BlockOperation2- (IBAction)tapBlockOperation:(id)sender {3__weak __block ViewController *copy_self =self;45//创建BlockOperation6NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{7[copy_self loadImageWithThreadName:@"Block"];8}];910//添加到操作队列11NSOperationQueue *operationQueue =[[NSOperationQueue alloc] init];12[operationQueue addOperation:blockOperation];1314for(inti =0;i <10; i ++) {1516//另一种方式17[operationQueue addOperationWithBlock:^{18[copy_self loadImageWithThreadName:@"Block"];19}];20}21}

5.GCD中的串行队列:

1//串行队列2- (IBAction)tapGCDserialQueue:(id)sender {3//创建串行队列4dispatch_queue_t serialQueue = dispatch_queue_create("mySerialQueue", DISPATCH_QUEUE_SERIAL);567__weak __block ViewController *copy_self =self;8910for(inti =0;i <10; i ++) {11//异步执行队列12dispatch_async(serialQueue, ^{13[copy_self loadImageWithThreadName:@"Serial"];14});15}161718}

6.GCD中的并行队列:

1//并行队列2- (IBAction)tapGCDConcurrentQueue:(id)sender {3//创建并行队列4dispatch_queue_t concurrentQueue = dispatch_queue_create("myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);5__weak __block ViewController *copy_self =self;67for(inti =0;i <10; i ++) {8//异步执行队列9dispatch_async(concurrentQueue, ^{10[copy_self loadImageWithThreadName:@"Concurrent"];11});1213}1415}

以上是各个按钮对应的方法,下面的截图是执行结果:

三、线程间的同步问题(为我们的线程添加上同步锁)

在操作系统中讲多线程时有一个名词叫脏数据,就是多个线程操作同一块资源造成的,下面就修改一下代码,让数据出现问题,然后用同步锁来解决这个问题

1.在getImageData方法(标题一中的第3个方法)中有两条语句。这个用来显示线程的标号。上面的标号是没有重复的。

1_count ++;2intcount = _count;

在两条语句中间加一个延迟,如下:

_count ++;

[NSThread sleepForTimeInterval:1];intcount = _count;

如果运行的话,会有好多标号是重复的,如图一,__count是成员变量,多个线程对此他进行操作,所以会出现标号不一致的情况,下面我们加上同步锁

(1)用NSLock加同步锁,代码如下:

1//通过NSLock加锁2[_locklock];3_count ++;4[NSThread sleepForTimeInterval:1];5intcount =_count;6[_lock unlock];

(2)通过@synchronized加同步锁,代码如下:

1//通过synchronized加锁2intcount;3@synchronized(self){4_count ++;5[NSThread sleepForTimeInterval:1];6count =_count;7}

加锁前后的运行效果如下:

GCD的串行队列开始执行的顺序如下,下面是是在一个线程中按FIFO的顺序执行的:

GCD中的并行队列,是在不同的线程中同时执行的:

今天博客中的内容还是蛮多的,如果之前接触过java的多线程的东西,或者其他语言中的多线程的话,理解起来应该问题不大。

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航