A1_多线程NSThread、GCD、NSOperation
一、多线程的优缺点
优点:能适当提高程序执行效率,能提高资源利用率(CPU、内存利用率)。
缺点:开启线程需要内存空间,线程越多,CPU调度线程开销越多,使程序设计更加复杂。
二、iOS中的多线程
1、NSThread:轻量级别的多线程技术,初始化方式手动开辟内存需要启动,构造器方式自动启动线程。线程使用完毕后的资源回收。NSThread 需要我们自己去管理线程的生命周期,还要考虑线程同步、加锁问题,GCD和NSOperationQueue不需要考虑。
//初始化方法创建线程需要start启动线程。
NSThread *thread = [[NSThread alloc] initwithTarget:self selector:@selector(testThread:) object:@"我是参数"];
[thread start];
thread.threadPriority = 1;//调整线程权限,范围0-1,权限越大执行概率越高。
[thread cancel];
//构造器创建线程
[NSThread detachNewThreadSelector:@selector(testThread:) toTarget:self withObject:@"构造器方式参数"];
performSelector...只要是 NSObject 的子类或者对象都可以通过调用方法进入子线程和主线程,其实这些方法 所开辟的子线程也是 NSThread 的另一种体现方式。
//performSelector:withObject:只是一个单纯的消息发送,和时间没有一点关系。所以不需要添加到子线程的 Runloop 中也能执行.
[self performSelector:@selector(testThread:) withObject:@"参数" ];
//回到主线程,waitUntilDone是否在该回调方法执行完后再执行后面的代码,如果为yes就必须等回调方法执行完之后才能执行。
[self performSelectorOnMainThread:@selector(testThread:) withObject:@"参数" waitUntilDone:YES];
//开辟子线程
[self performSelectorInBackground:@selector(testThread:) withObject:@"参数" ];
//在指定线程执行
[self performSelector:@selector(testThread:) onThread:[NSThread currentThread] withObject:@"参数" waitUntilDone:YES];
//在当前线程中,延迟1s执行,体现了OC语言的动态性,在运行时才绑定方法。
[self performSelector:@selector(testThread:) withObject:@"参数" afterDelay:1 ];
[[NSRunLoop currentRunLoop] run];
//
如果是带 afterDelay 的延时函数,会在内部创建一个 NSTimer,然后添加到当前线程的 Runloop 中。也就是如果当前线程没有开启 runloop,该方法会失效。在子线程中,需要启动 runloop(注意调 用顺序)。
2.GCD对比NSOperationQueue
GCD 是面向底层的 C 语言的 API,NSOpertaionQueue 用 GCD 构建封装的,是 GCD 的高级抽象。
1、GCD 执行效率更高,而且由于队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构,写起来更方便。
2、GCD 只支持 FIFO 的队列,而 NSOperationQueue 可以通过设置最大并发数,设置优先级,添加依赖关系 等调整执行顺序。
3、NSOperationQueue 甚至可以跨队列设置依赖关系,但是 GCD 只能通过设置串行队列,或者在队列内添 加 barrier(dispatch_barrier_async)任务,才能控制执行顺序。
4、NSOperationQueue 因为面向对象,所以支持 KVO,可以监测 operation 是否正在执行(isExecuted)、是否结束(isFinished)、是否取消(isCanceld)。
3.GCD---队列
GCD 共有三种队列类型: main queue:通过 dispatch_get_main_queue()获得,这是一个与主线程相关的串行队列。
global queue:全局队列是并发队列,由整个进程共享。存在着高、中、低三种优先级的全局队列。调用 dispath_get_global_queue 并传入优先级来访问队列。
自定义队列:通过函数 dispatch_queue_create 创建的队列
4.GCD 任务执行顺序
1、串行队列先异步后同步
2、performSelector
dispatch_async(dispatch_get_global_queue(0,0)), ^{
[self performSelector:@selector(testThread:) withObject:@"参数" afterDelay:0];
});
这里的 test 方法是不会去执行的,原因在于这个方法要创建提交任务到 runloop 上的,而 gcd 底层创建的线程是默认没有开启对应 runloop 的,所有这 个方法就会失效。 而如果将 dispatch_get_global_queue 改成主队列,由于主队列所在的主线程是默认开启了 runloop 的,就会 去执行(将 dispatch_async 改成同步,因为同步是在当前线程执行,那么如果当前线程是主线程,test 方法也 是会去执行的)。
5.dispatch_barrier_async栅栏函数
1、提交一个栅栏函数在执行中,它会等待栅栏函数执行完。
2、提交一 个栅栏函数在异步执行中,它会立马返回。
3、而 dispatch_barrier_sync 和 dispatch_barrier_async 的区别也就在于会不会阻塞当前线程。
设计多读单写:可以多个读者同时读取数据,而在读的时候,不能取写入数据。并且,在写的过程 中,不能有其他写者去写。即读者之间是并发的,写者与读者或其他写者是互斥的。
-(id)readDataForKey:(NSString *)key {
__block id result;
dispatch_sync(_concurrentQueue,^{
result = [self valueForKey:key];
} );
}
-(void)writeData:(id)data forKey:(NSString*)key {
dispatch_barrier_async(_concurrentQueue,^{
[self setValue:data forKey:key];
});
}