iOS之多线程
2016-05-06 20:12
387 查看
程序:由源代码生成的可执行应用
进程:一个正在运行的程序可以看做一个进程,进程拥有独立运行所需的全部资源
线程:程序中独立运行的代码段
一个进程是由一个或多个线程组成,进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行
单线程:
每个正在运行的程序(进程),至少包含一个线程,这个线程叫主线程
主线程在程序启动时被创建,用于执行main函数
只有一个主线程的程序,称作单线程程序
在但线程程序中,主线程负责执行程序的所有代码(UI展现,刷新,网络请求,本地存储等等),这些代码只能顺序执行,无法并发执行
多线程:
拥有多个线程的程序,称作多线程程序
iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程
可以根据需要开辟若干个子线程
子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行(伪并发执行,是cpu在多个线程快速的跳跃)
主线程还被称为UI线程,数据的处理放在子线程中,UI的刷新放在主线程中
使用多线程的优点 : 资源利用率更好,程序设计在某些情况下更简单,程序响应更快
使用多线程的缺点 : 多线程尽管提升了性能但是存在一些访问限制,比如线程同步,线程互斥等; 多线程在使用用的时候, 最终要回到主线程刷新UI的, 如果开辟过多的多线程, 会造成CPU的消耗
单线程和多线程的区别:
单线程程序:只有一个线程,即主线程,代码顺序执行,容易出现代码阻塞(页面假死)
多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能
注意:iOS中关于UI的添加和刷新必须在主线程中操作,是为了防止多个线程同时操作UI
NSThread是一个轻量级的多线程,它有两种创建方式
第一种:初始化方式
常用方法
NSThread需要注意的有一下几点
每个线程都维护找与自己对应的NSAutoreleasePool对象,将其放在线程栈的栈顶,当线程结束时,会清空自动释放池
为保证对象的及时释放,在多线程方法中需要添加自动释放池
在应用程序打开的时候,系统会为主线程创建一个自动释放池
我们手动创建的子线程需要我们手动添加自动释放池
NSOperation和NSOperationQueue
NSOperation
NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类
因为它是抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation和NSBlockOperation)来执行实际任务
NSOperation(含子类),只是一个操作,本身无主线程,子线程之分,可在任意线程中使用,通常与NSOperationQueue结合使用
NSInvocationOperation
NSInvocationOperation是NSOperation的子类
封装了执行操作的target和要执行的action
NSBlockOperation
NSBlockOperation是NSOperation的子类
封装了需要执行的代码块
NSOperationQueue
NSOperationQueue是操作队列,用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行
其中NSOperation可以调节它在队列中的优先级(使用addDependency:设置依赖关系)
当最大并发数设置为1的时候,能实现线程同步(串行执行)
GCD(Grand Central Dispatch)
Grand Central Dispatch是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称多处理系统
GCD提供函数实现多线程开发,性能更高,功能更强大
它首次发布在Mac OS X 10.6, iOS 4及以上也可用
任务:具有一定功能的代码段. 一般是一个block或者函数
分发队列:GCD以队列的方式进行工作,遵守FIFO
GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务
dispatch queue分为以下两种
SerialQueue: 一次只执行一个任务.Serial Queue通常用于同步访问特定的资源或数据.当你创建多个serial Queue时,虽然它们各自是同步执行的,但Serial Queue与Serial Queue之间是并发执行的,Serial Queue能实现线程同步
Concurrent: 可以并发地执行多个任务,但是遵守FIFO
第一种方式
第二种方式
GCD功能
线程间通信
线程间的通信分为两种:
主线程进入子线程(前面的方法都可以)
子线程回到主线程
线程互斥
多线程并行编程中,线程间同步和互斥是一个很有技巧的也很容易出错的额地方
线程间互斥应对的是这种场景
多个线程操作同一个资源(即某个对象), 需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性的操作后, 状态仍然正确.
典型的例子是”售票厅售票应用”. 售票厅剩余100张票, 10个窗口去卖这些票,这10个窗口,就是10条线程, 售票厅就是他们共同操作的资源,其中剩余的100张票就是这个资源的一个状态,线程买票的过程失去递减这个剩余数量的过程.
线程互斥的解决方案
方法一 : @synchronized自动对参数对象加锁,保证临界区内的代码线程安全
方法二 : NSLock
方法三 : NSConditionLock 条件锁 可以设置条件
方法四 : NSRecursiveLock 递归锁 多次调用不会阻塞已获取该锁的线程
进程:一个正在运行的程序可以看做一个进程,进程拥有独立运行所需的全部资源
线程:程序中独立运行的代码段
一个进程是由一个或多个线程组成,进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行
单线程:
每个正在运行的程序(进程),至少包含一个线程,这个线程叫主线程
主线程在程序启动时被创建,用于执行main函数
只有一个主线程的程序,称作单线程程序
在但线程程序中,主线程负责执行程序的所有代码(UI展现,刷新,网络请求,本地存储等等),这些代码只能顺序执行,无法并发执行
多线程:
拥有多个线程的程序,称作多线程程序
iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程
可以根据需要开辟若干个子线程
子线程和主线程都是独立的运行单元,各自的执行互不影响,因此能够并发执行(伪并发执行,是cpu在多个线程快速的跳跃)
主线程还被称为UI线程,数据的处理放在子线程中,UI的刷新放在主线程中
使用多线程的优点 : 资源利用率更好,程序设计在某些情况下更简单,程序响应更快
使用多线程的缺点 : 多线程尽管提升了性能但是存在一些访问限制,比如线程同步,线程互斥等; 多线程在使用用的时候, 最终要回到主线程刷新UI的, 如果开辟过多的多线程, 会造成CPU的消耗
单线程和多线程的区别:
单线程程序:只有一个线程,即主线程,代码顺序执行,容易出现代码阻塞(页面假死)
多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能
注意:iOS中关于UI的添加和刷新必须在主线程中操作,是为了防止多个线程同时操作UI
iOS平台下的多线程
NSThreadNSThread是一个轻量级的多线程,它有两种创建方式
第一种:初始化方式
- (void)threadMethod { //初始化tread 当线程是我们手动开辟的时候,需要我们自己来管理内存 //Target在谁里面实现填谁 //object : 回调方法的参数 myThread = [[NSThread alloc] initWithTarget:self selector:@selector(forAction) object:nil]; //为开辟的子线程命名 myThread.name = @"first"; //如果是使用初始化的方式创建的子线程,那么需要我们手动开启线程 [myThread start]; //取消当前线程 取消线程之后,子线程中的代码照样会执行,我们需要在合适的地方使用[myThread canceled]来判断当前线程是否被取消 // [myThread cancel]; }
- 第二种:遍历构造器
//for循环按钮 - (void)forAction { //当代码片段是在我们自己手动开辟的线程中执行的,需要我们手动来管理内存,这个时候,我们需要将代码片段放在自动释放池中 @autoreleasepool { // for (int i = 0; i < 100; i++) { // //线程被取消,但是代码还在执行,所以需要进行以下的判断 // if (myThread.cancelled) { // break; // } // NSLog(@"*******%@",[NSThread currentThread]); // } NSLog(@"我是子线程---%@",[NSThread currentThread]); //通过NSObject提供的方法回主线程 //withObject:回调方法的参数 //waitUntilDone:如果是YES,那么就会回调方法中所有的代码执行完成了,才会执行当前线程剩余的代码,如果为NO,那么就不会等待回调方法中的代码执行完成 //如果底下剩余代码跟这行代码有没有依赖关系 [self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:NO]; NSLog(@"疯狂的萨克斯"); } }
常用方法
//获取当前线程 [NSThread currentThread]; //获取主线程 [NSThread mainThread]; //线程休眠2秒 [NSThread sleepForTimeInterval:2];
NSThread需要注意的有一下几点
每个线程都维护找与自己对应的NSAutoreleasePool对象,将其放在线程栈的栈顶,当线程结束时,会清空自动释放池
为保证对象的及时释放,在多线程方法中需要添加自动释放池
在应用程序打开的时候,系统会为主线程创建一个自动释放池
我们手动创建的子线程需要我们手动添加自动释放池
NSOperation和NSOperationQueue
NSOperation
NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类
因为它是抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation和NSBlockOperation)来执行实际任务
NSOperation(含子类),只是一个操作,本身无主线程,子线程之分,可在任意线程中使用,通常与NSOperationQueue结合使用
NSInvocationOperation
NSInvocationOperation是NSOperation的子类
封装了执行操作的target和要执行的action
//operation的学习,operation本身跟线程无关,只是一个任务操作 - (void)operationStudy { //target - action方式创建一个任务 NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printAction) object:nil]; //如果任务不在队列中,那么就需要我们手动启动任务 [invocationOperation start]; } //任务的回调方法 - (void)printAction { NSLog(@"当前线程为%@",[NSThread currentThread]); }
NSBlockOperation
NSBlockOperation是NSOperation的子类
封装了需要执行的代码块
- (void)blockOperationStudy { //block的方式创建一个任务 NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"当前线程是:%@,是否主线程:%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]); }]; for (int i = 0; i < 5; i++) { //当使用addExecutionBlock为当前任务添加额外的block的时候,那么此block所在的线程就是在当前线程或者其他线程 [blockOperation addExecutionBlock:^{ NSLog(@"excu-----%d-----%@",i,[NSThread currentThread]); }]; } //收尾 blockOperation.completionBlock = ^(){ NSLog(@"我就想看结果"); }; //开始执行 [blockOperation start]; }
NSOperationQueue
NSOperationQueue是操作队列,用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行
其中NSOperation可以调节它在队列中的优先级(使用addDependency:设置依赖关系)
当最大并发数设置为1的时候,能实现线程同步(串行执行)
//NSOperationQueue学习,操作队列,实现多线程的一种技术手段,thread需要手动管理内存,队列不需要,thread每次只能开辟一个子线程,队列可以调度,分配,管理多个线程 - (void)operationQueueStudy { //创建一个其他队列,通过初始化的方式创建的队列只能是其他队列 NSOperationQueue *otherOperationQueue = [[NSOperationQueue alloc] init]; //队列中只能添加任务 //创建一个任务 NSBlockOperation *blockOperation_1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"当前所在线程1%@",[NSThread currentThread]); }]; NSBlockOperation *blockOperation_2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"当前所在线程2%@",[NSThread currentThread]); }]; NSBlockOperation *blockOperation_3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"当前所在线程3%@",[NSThread currentThread]); }]; NSBlockOperation *blockOperation_4 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"当前所在线程4%@",[NSThread currentThread]); }]; NSBlockOperation *blockOperation_5 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"当前所在线程5%@",[NSThread currentThread]); }]; //希望某几个任务执行的时候有先后关系,那么我们就可以为任务添加依赖,不能互相添加为依赖关系 [blockOperation_1 addDependency:blockOperation_5]; //通过设置最大并发数,让一组任务可以有序的执行,最大并发数的默认值为-1,意思为无限 // otherOperationQueue.maxConcurrentOperationCount = 1; //将任务添加到队列,在队列中的任务不需要手动启动 [otherOperationQueue addOperation:blockOperation_1]; [otherOperationQueue addOperation:blockOperation_2]; [otherOperationQueue addOperation:blockOperation_3]; [otherOperationQueue addOperation:blockOperation_4]; [otherOperationQueue addOperation:blockOperation_5]; //主队列 [self mainOperationQueue]; }
#pragma mark - operationQueue主队列 //主队列的作用:返回主线程 - (void)mainOperationQueue { NSLog(@"当前线程为----------------%@",[NSThread currentThread]); //先获取主队列,在主队列的任务都是在主线程中执行的,其他队列中的任务都是在子线程中执行 NSOperationQueue *myMainQueue = [NSOperationQueue mainQueue]; //添加任务 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"-------1%@",[NSThread currentThread]); }]; NSBlockOperation *blockOperation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"-------2%@",[NSThread currentThread]); }]; NSBlockOperation *blockOperation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"-------3%@",[NSThread currentThread]); }]; //最大并发数 myMainQueue.maxConcurrentOperationCount = 1; //把任务添加到主队列中 [myMainQueue addOperation:blockOperation1]; [myMainQueue addOperation:blockOperation2]; [myMainQueue addOperation:blockOperation3]; }
GCD(Grand Central Dispatch)
Grand Central Dispatch是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称多处理系统
GCD提供函数实现多线程开发,性能更高,功能更强大
它首次发布在Mac OS X 10.6, iOS 4及以上也可用
任务:具有一定功能的代码段. 一般是一个block或者函数
分发队列:GCD以队列的方式进行工作,遵守FIFO
GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务
dispatch queue分为以下两种
SerialQueue: 一次只执行一个任务.Serial Queue通常用于同步访问特定的资源或数据.当你创建多个serial Queue时,虽然它们各自是同步执行的,但Serial Queue与Serial Queue之间是并发执行的,Serial Queue能实现线程同步
//gcd的串行队列 有序的执行一组任务 - (void)serialQueue { //创建一个串行队列 //第一个参数:当前队列的标签 //第二个参数:当前队列的类型 dispatch_queue_t serialQueue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL); //创建任务 //第一个参数:当前任务所在的队列 //第二个参数:任务的block回调,逻辑代码在block中 dispatch_async(serialQueue, ^{ NSLog(@"马克打鸟窝"); NSLog(@"---------%@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"抓到鸟蛋烤着吃"); NSLog(@"---------%@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"一个不够吃两个"); NSLog(@"---------%@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"两个不够继续打"); NSLog(@"---------%@",[NSThread currentThread]); }); dispatch_async(serialQueue, ^{ NSLog(@"鸟蛋没了"); NSLog(@"---------%@",[NSThread currentThread]); }); }
Concurrent: 可以并发地执行多个任务,但是遵守FIFO
第一种方式
//gcd的并行队列 - (void)concurrent { dispatch_queue_t concurrentQueue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT); //通过回调函数的方式来实现任务 //第二个参数:回调函数的参数 dispatch_async_f(concurrentQueue, @"赵薇和乐乐开始愉快的生活", function); dispatch_async(concurrentQueue, ^{ NSLog(@"赵薇和乐乐睡了"); NSLog(@"---------%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ NSLog(@"赵薇哭,乐乐郁闷的抽烟"); NSLog(@"---------%@",[NSThread currentThread]); }); dispatch_async(concurrentQueue, ^{ NSLog(@"赵薇去了泰国,为了真爱"); NSLog(@"---------%@",[NSThread currentThread]); }); } //声明一个函数 void function(void* content) { NSLog(@"%@",content); NSLog(@"---------%@",[NSThread currentThread]); }
第二种方式
//在实际使用中我们常用GCD的方式: - (void)usefulMethod { //系统提供的一个全局队列 //第一个参数:全局队列的优先级 //第二个参数:预留参数,当前并没有用 dispatch_queue_t oneQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(oneQueue, ^{ //在此处执行耗时的操作....... //当操作执行完成之后我们需要回到主线程刷新UI,gcd回主线程的方式 dispatch_async(dispatch_get_main_queue(), ^{ //已经回到主线程,在此处刷新UI }); }); }
GCD功能
- (void)otherFunctionsOfGCD { // dispatch_group_t 主要用于把一些不想关的任务归为一组 // 组里面放的是队列 // dispatch_group_async 作用是往组里面的队列添加任务 // dispatch_group_notify 作用: 监听组里面的任务,等到组里面的任务全部执行完成之后,才会执行其他任务 //1.创建队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //2.创建组 dispatch_group_t group = dispatch_group_create(); //3.往组里面的队列添加任务(注意:在执行notify之前最起码要向队列中放置一个任务才可以,否则,notify里面的任务不会等待小组里面的其他任务执行完成才执行) dispatch_group_async(group, queue, ^{ NSLog(@"这是第一个任务,线程是:%@, 是否主线程%d",[NSThread currentThread],[[NSThread currentThread] isMainThread]); }); dispatch_group_notify(group, queue, ^{ NSLog(@"这是最后一个任务,组里面的其他任务都执行完毕之后,我就会执行"); }); }
- (void)sqliteAndGCD { //数据库的读取 可以并发执行通过GCD里面的并行队列去实现 //数据库的写入 只能串行执行,通过GCD里面的串行队列去实现 //但是真正的项目,肯定是既有数据的读取,也有数据库的写入,如何解决这个问题: dispatch_barrier_async 在它之前的任务可以并发去执行,再他之后的任务也可以并发去执行 dispatch_queue_t queue = dispatch_queue_create("concurrentTest", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"这是第一个读取数据的任务 线程是:%@, 是否主线程: %d",[NSThread currentThread],[[NSThread currentThread] isMainThread]); }); dispatch_barrier_async(queue, ^{ NSLog(@"哥哥我正在给数据库写东西"); }); dispatch_async(queue, ^{ NSLog(@"这是第二个读取数据的任务 线程是: %@, 是否主线程: %d",[NSThread currentThread],[[NSThread currentThread] isMainThread]); }); }
//dispatch_once : 该函数接收一个dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为BOOL使用). 它还接收一个希望在应用的声明周期内仅被调度一次的代码块 //dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要诸如@synchronize之类的来防止使用多个线程或者队列时不同步的问题,通常用于单例 //static SharedData *data = nil; //+ (SharedData*)defaultData { // //block中的代码只会执行一次 // static dispatch_once_t onceToken; // dispatch_once(&onceToken, ^{ // data = [[SharedData alloc] init]; // }); // return data; //} //async 不等block执行完,就去执行下面的代码 //sync 会等待block执行完成之后 才会执行block外面的代码 - (void)asyncAndSync { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_sync(queue, ^{ NSLog(@"第一个任务"); }); NSLog(@"呵呵"); dispatch_async(queue, ^{ NSLog(@"第二个任务"); }); NSLog(@"哈哈"); }
线程间通信
线程间的通信分为两种:
主线程进入子线程(前面的方法都可以)
子线程回到主线程
//子线程回到主线程: //第一种方法: dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执⾏耗时的异步操作... dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程,执⾏UI刷新操作 }); }); //第二种方法: [self performSelectorOnMainThread:@selector(backMainThread) withObject:nil waitUntilDone:NO];
线程互斥
多线程并行编程中,线程间同步和互斥是一个很有技巧的也很容易出错的额地方
线程间互斥应对的是这种场景
多个线程操作同一个资源(即某个对象), 需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性的操作后, 状态仍然正确.
典型的例子是”售票厅售票应用”. 售票厅剩余100张票, 10个窗口去卖这些票,这10个窗口,就是10条线程, 售票厅就是他们共同操作的资源,其中剩余的100张票就是这个资源的一个状态,线程买票的过程失去递减这个剩余数量的过程.
线程互斥的解决方案
方法一 : @synchronized自动对参数对象加锁,保证临界区内的代码线程安全
方法二 : NSLock
方法三 : NSConditionLock 条件锁 可以设置条件
方法四 : NSRecursiveLock 递归锁 多次调用不会阻塞已获取该锁的线程
相关文章推荐
- 单例
- iOS 图片设置为圆角矩形,圆形等
- 我写过的软件之UniversalTest for iOS
- iOS 8 AutoLayout与Size Class
- iOS内存管理(一)
- iOS开发——创建你自己的Framework
- iOS NSDecimalNumber--货币计算
- iOS开发:音频播放、录音、视频播放、拍照、视频录制
- iOS RunLoop
- iOS 归档、解档
- iOS&nbsp;中KVC、KVO、NSNotificat…
- iOS开发&nbsp;引用第三方库出现duplicat…
- iOS&nbsp;KVC和KVO开发模式
- iOS各种权限状态获取
- iOS 多线程 - NSOperation
- iOS-键盘弹出的类型
- ios中常用数据类型相互转换
- iOS block的简单说明
- iOS中几种数据持久化方案
- iOS实现表格(非TableView)