概述
在这里主要讲述线程的基础知识和创建线程的几种方式如:NSThread,NSOPeration 和GCD.以及使用方法和注意事项
一、进程和线程的区别以及线程基础知识
1.进程:系统运行的一个个应用程序,每个程序就是一个进程,进程之间是相互独立的,每个进程运行在其受保护的内存空间内
2.线程:进程要运行起来至少有一个线程存在,线程是进程的执行单元,一个进程的所有任务都在进程中执行,应用程序启动时系统会默认帮我们开启一条线程,叫“主线程“或”UI线程“。
3.线程串行:一个线程中执行任务是串行的,也就是说如果执行多个任务是一个一个按顺序来的。
4.多线程
4.1.多线程概念:一个应用程序想要执行更多的任务需要进程开辟多个线程,这些线程是并行的,多线程提高了应用程序的执行速率
4.2.多线程原理:同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
4.3.多线程优点:能适当提高程序的执行效率。能适当提高资源利用率(CPU、内存利用率)。
4.4多线程缺点:开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能线程越多,CPU在调度线程上的开销就越大程序设计更加复杂:比如线程之间的通信、多线程的数据共享
主线程的使用注意:别将比较耗时的操作放到主线程中。
二、NSThread
优点:比其他两个轻量级创建线程
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
1.创建并启动
// 创建
NSThread*thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(run:) object:nil];
// 启动
[thread start];
2.创建创建并自动启动
[NSThreaddetachNewThreadSelector:@selector(run:) toTarget:selfwithObject:nil];
3.方法
//取消线程- (void)cancel;
//启动线程- (void)start;
//判断某个线程的状态的属性@property(readonly, getter=isExecuting)BOOLexecuting;@property(readonly, getter=isFinished)BOOLfinished;@property(readonly, getter=isCancelled)BOOLcancelled;
//设置和获取线程名字-(void)setName:(NSString*)n;-(NSString*)name
;//获取当前线程信息+ (NSThread*)currentThread;
//获取主线程信息+ (NSThread*)mainThread;
//使当前线程暂停一段时间,或者暂停到某个时刻+ (void)sleepForTimeInterval:(NSTimeInterval)time;+ (void)sleepUntilDate:(NSDate*)date;
三、GCD
Grand Central Dispatch 可译为“牛逼的中枢调度器”,GCD是苹果公司为多核的并行运算提出的解决方案,会自动利用更多的CPU内核(比如双核、四核),会自动管理线程的生命周期(创建线程、调度任务、销毁线程),并且程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码。纯c语言,提供了非常多强大的函数。
提示:(1)GCD存在于libdispatch.dylib这个库中,这个调度库包含了GCD的所有的东西,但任何IOS程序,默认就加载了这个库,在程序运行的过程中会动态的加载这个库,不需要我们手动导入 (2)GCD是纯C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法 (3)GCD中的函数大多数都以dispatch开头。
任务和队列
任务
就是操作,你想干的事情,就是一段代码,在GCD中就是一个BLock.添加任务很方便有两种执行方式:并行执行 串行执行,他们的区别是会不会创建新线程。
同步执行:会阻塞当前的线程,并等Block中的任务执行完,然后当前的线程才会向下执行。
异步执行:当前线程会向下执行,不会阻塞当前线程。
队列
用来存放任务,分为串行队列和并行队列
串行队列:(Serial Dispatch Queue)GCD 会FIFO(先进先出)地取出来一个,执行一个,然后取下一个,这样一个一个的执行。
并行队列:(Concurrent Dispatch Queue)放到并行队列的任务,GCD 也会FIFO的取出来,但不同的是,它取出来一个就会放到别的线程,然后再取出来一个又放到另一个的线程。
GCD中获得串行有2种途径
(1)使用dispatch_queue_create函数创建串行队列
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL即可
示例:
dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 创建
dispatch_release(queue); // 非ARC需要释放手动创建的队列
dispatch_queue_create可以创建串行和并行队列,第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。第二个参数用来表示创建的队列是串行的还是并行的,传入DISPATCH_QUEUE_SERIAL或NULL表示创建串行队列。传入DISPATCH_QUEUE_CONCURRENT表示创建并行队列。
(2)使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列
示例:
dispatch_queue_t queue = dispatch_get_main_queue();
GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建
使用dispatch_get_global_queue函数获得全局的并发队列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此参数暂时无用,用0即可
四、NSOperation 和 NSOperationQueue
NSOperation
是苹果公司对 GCD 的封装,完全面向对象,NSOperation 和NSOperationQueue分别对应 GCD 的任务 和 队列。操作步骤:
1.将要执行的任务封装到一个NSOperation对象中。
2.将此任务添加到一个NSOperationQueue对象
添加任务
NSOperation是个抽象类,并不具备封装操作的能力,必须使⽤它的子类
使用NSOperation⼦类的方式有3种:(1)NSInvocationOperation(2)NSBlockOperation (3)自定义子类继承NSOperation,实现内部相应的⽅法
NSInvocationOperation : 需要传入一个方法名。
//1.创建NSInvocationOperation对象
NSInvocationOperation*operation = [[NSInvocationOperationalloc] initWithTarget:selfselector:@selector(run) object:nil];//2.开始执行[operation start];
NSBlockOperation
//1.创建NSBlockOperation对象
NSBlockOperation*operation = [NSBlockOperationblockOperationWithBlock:^{NSLog(@"%@", [NSThreadcurrentThread]); }];
//2.开始任务[operation start];
NSBlockOperation还有一个方法:addExecutionBlock:,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务会并发执行,它会在主线程和其它的多个线程执行这些任务
自定义Operation
除了上面的两种 Operation 以外,我们还可以自定义 Operation。自定义 Operation 需要继承NSOperation类,并实现其main()方法,因为在调用start()方法的时候,内部会调用main()方法完成相关逻辑。你想要实现什么功能都可以写在里面。除此之外,你还需要实现cancel()在内的各种方法。
创建队列
主队列
每套多线程方案都会有一个主线程这是一个特殊的线程,必须串行。所以添加到主队列的任务都会一个接一个地排着队在主线程处理。
NSOperationQueue*queue = [NSOperationQueuemainQueue];
其他队列
//1.创建一个其他队列NSOperationQueue*queue = [[NSOperationQueuealloc] init];
//2.创建NSBlockOperation对象NSBlockOperation*operation = [NSBlockOperationblockOperationWithBlock:^{NSLog(@"%@", [NSThreadcurrentThread]);}];
//3.添加多个Blockfor(NSIntegeri =0; i <5; i++) { [operation addExecutionBlock:^{NSLog(@"第%ld次:%@", i, [NSThreadcurrentThread]); }];}
//4.队列添加任务[queue addOperation:operation];
NSOperationQueue有一个参数maxConcurrentOperationCount最大并发数,用来设置最多可以让多少个任务同时执行。当你把它设置为1的时候,就是串行
NSOperationQueue还有一个添加任务的方法,- (void)addOperationWithBlock:(void (^)(void))block;,这样就可以添加一个任务到队列中
NSOperation有一个非常实用的功能,那就是添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:
注意:不能添加相互依赖,会死锁,比如 A依赖B,B依赖A。
可以使用removeDependency来解除依赖关系。
可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没关系
其他方法
以上就是一些主要方法, 下面还有一些常用方法需要大家注意:
NSOperation
BOOL executing; //判断任务是否正在执行
BOOL finished; //判断任务是否完成
void (^completionBlock)(void); //用来设置完成后需要执行的操作
- (void)cancel; //取消任务
- (void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕
NSOperationQueue
NSUInteger operationCount; //获取队列的任务数
- (void)cancelAllOperations; //取消队列中所有的任务
- (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕
[queue setSuspended:YES]; // 暂停queue
[queue setSuspended:NO]; // 继续queue
线程同步
所谓线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全问题,所采取的一种措施。当然也有很多实现方法
互斥锁:给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。
@synchronized(self) {//需要执行的代码块}
延迟执行
所谓延迟执行就是延时一段时间再执行某段代码。下面说一些常用方法。
// 3秒后自动调用self的run:方法,并且传递参数:@"abc"0
[selfperformSelector:@selector(run:) withObject:@"abc"afterDelay:3];
从其他线程回到主线程的方法
NSThread
[selfperformSelectorOnMainThread:@selector(run) withObject:nilwaitUntilDone:NO];
GCD
dispatch_async(dispatch_get_main_queue(), ^{});
NSOperationQueue
[[NSOperationQueuemainQueue] addOperationWithBlock:^{}];