摘录:http://www.cnblogs.com/mjios/archive/2013/04/18/3029309.html
http://blog.csdn.net/totogo2010/article/details/8010231
简介
iOS有三种多线程编程的技术,分别是:
1.、NSThread :这种方法需要管理线程的生命周期、同步、加锁问题,会导致一定的性能开销
2、Cocoa NSOperation :是基于OC实现的。NSOperation以面向对象的方式封装了需要执行的操作,然后可以将这个操作放到一个NSOperationQueue中去异步执行。不必关心线程管理、同步等问题。
3、GCD 全称:Grand Central Dispatch,简称GCD,iOS4才开始支持,是纯C语言的API。自iPad2开始,苹果设备开始有了双核CPU,为了充分利用这2个核,GCD提供了一些新特性来支持多核并行编程
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 NSThread *thread = [[[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"userinfo"] autorelease];
3 // 开启线程
4 [thread start];
run:方法
- (void)run:(NSString *)string {
NSThread *current = [NSThread currentThread];
NSLog(@"执行了run:方法-参数:%@,当前线程:%@", string, current);
}
//结果
执行了run:方法-参数:userinfo,当前线程:<NSThread: 0x889e8d0>{name = (null), num = 3}
同步锁
创建锁可使用
NSLock *theLock = [[NSLock alloc] init];
已上面例子,对run:加锁
- (void)run:(NSString *)string {
[theLock lock];
NSThread *current = [NSThread currentThread];
NSLog(@"执行了run:方法-参数:%@,当前线程:%@", string, current);
[theLock unlock];
}
还有其他的一些锁对象,比如:循环锁NSRecursiveLock,条件锁NSConditionLock,分布式锁NSDistributedLock等等
2、NSOperation
(1).思路:
1> 先将需要执行的操作封装到一个NSOperation对象中
2> 然后将NSOperation对象添加到NSOperationQueue中
3> 系统会自动将NSOperation中封装的操作放到一条新线程中执行
在此过程中,我们根本不用考虑线程的生命周期、同步、加锁等问题
(2).创建
默认情况下,NSOperation并不具备封装操作的能力,必须使用它的子类,使用NSOperation子类的方式有3种:
1> NSInvocationOperation
2> NSBlockOperation
3> 自定义子类继承NSOperation,实现内部相应的方法
(3).使用
NSInvocationOperation例子:
NSInvocationOperation *operation = [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"userinfo"] autorelease];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
NSBlockOperation例子:
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行了一个新的操作");
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:operation];
若要并发执行多个任务,可使用NSBlockOperation
的addExecutionBlock:
方法添加了新的操作
[operation addExecutionBlock:^() {
NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);
}];
自定义NSOperation:
如果NSInvocationOperation和NSBlockOperation不能满足需求,我们可以直接新建子类继承NSOperation,并添加任何需要执行的操作。如果只是简单地自定义NSOperation,只需要重载-(void)main这个方法,在这个方法里面添加需要执行的操作。
注意:如果创建NSOperation后直接使用[operation start];
执行线程还是会在当前线程同步执行操作,并没有异步执行
(4).取消操作
operation开始执行之后, 默认会一直执行操作直到完成,我们也可以调用cancel方法中途取消操作
[operation cancel];
(5).其他
如何控制线程池中的线程数?
队列里可以加入很多个NSOperation, 可以把NSOperationQueue看作一个线程池,可往线程池中添加操作(NSOperation)到队列中。线程池中的线程可看作消费者,从队列中取走操作,并执行它。
通过下面的代码设置:
[queue setMaxConcurrentOperationCount:5];
线程池中的线程数,也就是并发操作数。默认情况下是-1,-1表示没有限制,这样会同时运行队列中的全部的操作。
3.GCD
GCD的工作原理是:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。
一个任务可以是一个函数(function)或者是一个block。 GCD的底层依然是用线程实现,不过这样可以让程序员不用关注实现的细节。
(1).队列
GCD 是异步任务的技术之一,开发者可以用它将自定义的任务(task)追加到适当的派发队列(dispatch queue),就能生成必要的线程并执行任务。
在 GCD 中有三种队列:主队列(main queue)、全局队列(global queue)、用户队列(user-created queue)。
*全局队列是并发队列,即队列中的任务(task)执行顺序和进入队列的顺序无关;
主队列是串行队列,队列中的任务按FIFO(first input first output,先进先出)的顺序执行。
用户队列是用户自己创建的队列,可创建串行的也可以创建并行的
*
队列创建
全局队列(global queue):
// 获取默认优先级的全局并发dispatch queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
第一个参数用于指定优先级,分别使用DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW两个常量来获取高和低优先级的两个queue;第二个参数目前未使用到,默认0即可
主队列(main queue):
dispatch_queue_t queue =dispatch_get_main_queue();//获取主线程队列
用户队列(user-created queue):
dispatch_queue_t serialQueue = dispatch_queue_create("cn.itcast.queue", NULL); //串行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("cn.itcast.queue", DISPATCH_QUEUE_CONCURRENT); //并行队列
(2).派发
a.同步派发dispatch_sync
使用例子:
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
});
同步派发,执行时,会阻塞调用它的线程,然后执行block的内容,执行完成才能继续往下执行。
注意:不用在主线程中,将任务同步派发到主线程队列,会造成死锁。
b.异步派发dispatch_async
使用例子:
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
异步派发,执行时,不会阻塞调用它的线程,直接执行block内容。
c.其他
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_barrier_async:是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行。
void dispatch_barrier_async( dispatch_queue_t queue, dispatch_block_t block);
其中,queue必须是用户通过dispatch_queue_create
创建的并行队列。如:dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);
(3)总结:
在使用GCD中,派发方式决定了改任务执行时,是否会阻塞调用线程。而执行的队列决定了该任务是串行执行还是并发执行。