您的位置:首页 > 移动开发 > IOS开发

iOS多线程

2016-05-07 13:00 417 查看
iOS有三种多线程编程技术,分别是:

1)NSThread

2)NSOperation

3)GCD(全称:Grand Central Dispatch)

这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的

三种方式的优缺点:

1)NSThread:

优点:NSThread比其他两个轻量级

缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

使用:

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
参数的意义:

selector:线程执行的方法,这个selector只能有一个参数,而且不能有返回值。

target:selector消息发送的对象

argument:传输target的唯一参数,也可以是nil

第一种方式会直接创建线程并且开始运行线程,第二种方式是先创建线程对象,然后再运行线程操作,在运行线程操作前可以设置线程的优先级等线程信息

2)NSOperation

优点:不需要关心线程管理、数据同步的事件,尽管把精力放在自己需要执行的操作上。

NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个字类:NSInvocationOperation 和 NSBlockOperation。

创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。

使用:

使用NSOperation的方式有两种

1)定义好两个子类:NSInvocationOperation 和 NSBlockOperation

2)另一种是继承NSOperation

NSInvocationOperation例子:

- (void)viewDidLoad
{
[super viewDidLoad];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self
selector:@selector(downloadImage:)
object:kURL];

NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
// Do any additional setup after loading the view, typically from a nib.
}

-(void)downloadImage:(NSString *)url{
NSLog(@"url:%@", url);
NSURL *nsUrl = [NSURL URLWithString:url];
NSData *data = [[NSData alloc]initWithContentsOfURL:nsUrl];
UIImage * image = [[UIImage alloc]initWithData:data];
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
}
-(void)updateUI:(UIImage*) image{
self.imageView.image = image;
}
代码注释:

1.viewDidLoad方法里可以看到我们用NSInvocationOperation建了一个后台线程,并且放到NSOperationQueue中

2.后台线程执行downloadImage方法。

3.downloadImage
方法处理下载图片的逻辑。下载完成后用performSelectorOnMainThread执行主线程updateUI方法。

updateUI 并把下载的图片显示到图片控件中。

第二中方式继承NSOperation

在.m文件中实现main方法,main方法编写要执行的代码即可

如何控制线程池中的线程数

队列里可加入很多个NSOperation,可以把NSOperationQueue看作一个线程池,可往线程池中添加操作到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。

通过下面代码实现:

[queue setMaxConcurrentOperationCount:5];
线程池中的线程数,也就是并发操作数。默认情况下是-1,-1代表没有限制,这样会同时运行队列中的全部的操作

3)GCD

GCD是Apple开发的一个多核编程的解决方法。在iOS4.0开始之后才能使用。GCD是一个替代诸如NSTherad,NSOperationQueue,NSInvocationOperation等技术的很高效和强大的技术。现在的iOS系统都升级到7了,所有不要担心该技术不能使用

GCD的介绍和使用

介绍:

GCD是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统。这建立在任务并行执行的线程池模式的基础上的,它首次发布在Mac OS X 10.6,iOS 4及以上也可用。

设计:

GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。

一个任务可用是一个函数(function)或者是一个block。GCD的底层依然是用线程实现,不过这样可用让程序员不用关注实现的细节。

GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行。

dispatch queue分为下面三种:

Serial

又称为private dispatch quests,同时只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queues时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的

Concurrent

又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的。

Main dispatch queue

它是全局可以的serial queue,它是在应用程序主线程上执行任务的。

dispatch如何使用

1.常用的方法dispatch_async

为了避免界面在处理耗时的操作时卡死,比如读取网络数据,IO,数据库读写等,我们会在另外一个线程中处理这些操作,然后通知主线更新界面。

用GCD实现这个流程的操作比前面介绍的NSThread NSOperation的方法都要简单。代码框架结构如下:

(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
});
再如加载图片:

NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
NSData * data = [[NSData alloc]initWithContentsOfURL:url];
UIImage *image = [[UIImage alloc]initWithData:data];
if (data != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}
});
GCD会自动根据任务在多核处理器上分配资源,优化程序。

系统给每一个应用程序提供了三个concurrent dispatch queues。这三个并发调度队列是全局的,它们只有优先级的不同。因为是全局的,我们不需要去创建。我们只需要通过函数dispatch_get_global_queue去得到队列,如下:

dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
这里也用到了系统默认就有一个串行队列main_queue:

dispatch_queue_t mainQ = dispatch_get_main_queue();
虽然dispatch queue是引用计数的对象,但是以上两个都是全局的队列,不用retain或release。

2、dispatch_group_async的使用

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"group1");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"group2");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:3];
NSLog(@"group3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"updateUi");
});
dispatch_release(group);
dispatch_group_async是异步的方法,运行后可以看到打印结果:

2012-09-25 16:04:16.737 gcdTest[43328:11303] group1
2012-09-25 16:04:17.738 gcdTest[43328:12a1b] group2
2012-09-25 16:04:18.738 gcdTest[43328:13003] group3
2012-09-25 16:04:18.739 gcdTest[43328:f803] updateUi


每一个一秒打印,当第三个任务执行后,upadteUi被打印

3.dispatch_barrier_async的使用

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成后才会执行

4.dispatch_apply的使用

执行某个代码片段N次

dispatch_apply(5, globalQ, ^(size_t index) {
// 执行5次
});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: