谈一谈iOS中的多线程
来源:互联网 发布:安卓网络聊天室破解版 编辑:程序博客网 时间:2024/06/05 06:15
首先,讲到多线程的话,首先要明确几个基本概念。
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位。
线程:是指程序在执行时,能执行代码的一个执行单位(CPU调度的一个基本单位)。线程是进程的一部分,又被称为轻权进程或轻量级进程。
多线程:多个线程并发执行的技术。
多线程的作用主要有:1、可以减少程序的相应时间;2、线程的创建和切换开销比进程小,提高CPU利用率;3、简化程序结构,是程序便于维护。
举个栗子说明一下:
我们可以把在桌子上吃饭看做一个进程,那么可以把吃饭看做一个线程(这里都没有主语,把“人”给省略了)。在这样的前提下,我们可以得到以下结果:
单进程:在一张桌子上吃饭单线程:一个人吃饭
多进程:多张桌子上都在吃饭多线程:多个人都在吃饭
单进程单线程:一个人在一张桌子上吃饭;
单进程多线程:多个人在一张桌子上吃饭;
多进程单线程(有多个进程每个进程中都是单线程):有多个人,每个人都拥有一张桌子,每个人都在自己的桌子上吃饭。
现在有个需求:实现一家人吃饭。
如果使用单进程单线程来实现:大家在一张桌子上吃饭,但是必须一个一个的吃,前一个人吃完,才能下一个人吃。如果你是单身狗,可以用这样的方式吃饭
如果使用多进程单线程来实现:大家每个人都配一张桌子,各自在自己的桌子上吃饭,那么问题来了,就是别人吃不到你桌子上的菜(不是完全没办法,可以起身去夹菜,但也会有进程之间通信不便的情况)。如果家里人不是很多,至于几个人吃饭还要搞好几张桌子,然后每张桌子搞几个相同的菜吗?
如果使用单进程多线程来实现:一张桌子,大家在同一张桌子上吃饭,每个菜大家都可以吃,但是这种情况下有个问题需要注意,如某盘菜特别好吃,大家都在吃这个菜(多个线程频繁获取共享的资源),那么可能会发生争抢冲突,实现过程中要协调好。
上述实例(借鉴别人的说法),一般情况下当然是用单进程多线程比较合适,当然并不是非要使用单进程,比如你家是个大家族,每次吃饭20多个人,一张桌子也坐不下的情况,或者你家有个小宝宝,那吃饭的时候给宝宝一个餐桌椅的情况,再或者就是有钱,就是任性,就是要每个人一张桌子的情况等;这些情况下用多进程也是没有问题的。
iOS中的4套多线程方案
1.Pthreads(不常用)
基于C语言的框架,是一套在很多操作系统上都通用的API,移植性强(。。。),在iOS中调用Pthreads时要用C语言函数,并且需要手动管理生命周期(这是重点,看到这个,就不想用了)。知道有这么个东西,不要被人拿这个秀一脸就行了。搬运代码:
//记得导入pthread头文件#import <pthread.h>- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { pthread_t thread; //创建一个线程并自动执行 pthread_create(&thread, NULL, start, NULL);}void *start(void *data) { NSLog(@"%@", [NSThread currentThread]); return NULL;}
2.NSThread (不常用)
NSThread经过了苹果封装,完全面向对象,但是它的生命周期还是需要我们手动管理(。。。),不过NSThread中有我们可以用到的函数,当调试程序需要获取当前线程及线程的各个属性时,可以通过[NSThread currentThread]来获取当前线程。
// 创建 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil]; // 启动 [thread start]; //创建并自动启动 [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
列举NSThread常用方法:
//取消线程- (void)cancel;//启动线程- (void)start;//判断某个线程的状态的属性@property (readonly, getter=isExecuting) BOOL executing;@property (readonly, getter=isFinished) BOOL finished;@property (readonly, getter=isCancelled) BOOL cancelled;//设置和获取线程名字-(void)setName:(NSString *)n;-(NSString *)name;//获取当前线程信息+ (NSThread *)currentThread;//获取主线程信息+ (NSThread *)mainThread;//使当前线程暂停一段时间,或者暂停到某个时刻+ (void)sleepForTimeInterval:(NSTimeInterval)time;+ (void)sleepUntilDate:(NSDate *)date;
3.GCD
GCD是苹果开发的一个多核编程的较新解决方案,是一个替代如NSThread等技术的高效和强大技术。会自动管理线程的生命周期(美滋滋),使用C语言来实现调度,并且会使用到Block(闭包),让GCD的使用更加方便和灵活。
要说GCD就不得不说两个非常重要的概念:任务 和 队列
· 任务:即操作,在GCD中就是Block的内容,所以添加任务非常方便,任务有两种执行方式,同步执行 和 异步执行。
同步和异步的区别在与同步操作会阻塞当前线程并等待任务(Block)执行完毕,当前线程才会继续运行。而异步缺不会。
·队列:用于存放任务,遵循先进先出(FIFO)原则。分为 串行队列 和 并行队列。
串行:取出一个执行一个,完事再取下一个。
并行:不同于串行的FIFO,并行队列的FIFO会取出一个任务加入到其他线程,每个任务依次取出,因为取出任务的耗时很少,所以会感觉任务在同时进行。
获取队列:
主队列:dispatch_get_main_queue()
获取全局并行队列:只要是并发任务一般都加入到这个队列,这是系统提供的。
dispatch_queue_t queue = dispatch_get_global-queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
自己创建的队列:其中第一个参数是标识符,用于 DEBUG 的时候标识唯一的队列,可以为空。
//串行队列 dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL); dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL); //并行队列 dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
创建任务
同步任务:会阻塞当前线程
dispatch_sync(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]);});
异步任务:不会阻塞当前线程
dispatch_async(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]);});
· 队列组:队列组可以将很多队列添加到一个组里,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。上代码:
//1.创建队列组dispatch_group_t group = dispatch_group_create();//2.创建队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//3.多次使用队列组的方法执行任务, 只有异步方法//3.1.执行3次循环dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i < 3; i++) { NSLog(@"group-01 - %@", [NSThread currentThread]); }});//3.2.主队列执行8次循环dispatch_group_async(group, dispatch_get_main_queue(), ^{ for (NSInteger i = 0; i < 8; i++) { NSLog(@"group-02 - %@", [NSThread currentThread]); }});//3.3.执行5次循环dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"group-03 - %@", [NSThread currentThread]); }});//4.都完成后会自动通知dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"完成 - %@", [NSThread currentThread]);});
GCD中还有一个很常用的函数:dispatch_barrier_async
这个方法重点是传入的queue,当传入的queue是通过 DISPATCH_QUEUE_CONCURRENT 参数自己创建的queue时,这个方法会阻塞这个queue(注意是阻塞queue,而不是阻塞当前线程),一直等在这个queue中排在它前面的任务都执行完成才会开始执行自己,自己执行完毕,再回取消阻塞,使这个queue中排在它后面的任务继续执行。如果传入的是其他queue,那么它就和dispatch_async一样了。
假设我们原先有6个任务要执行,我们现在要插入一个任务0,这个任务0要在1、2、3都并发执行完了之后才能执行,而4、5、6号任务要在这个任务0结束后才允许并发。对于这样一种需求,很多朋友的第一反应就是用个group就解决了。确实如此,但是系统提供了一种更加简单地方法,那就是dispatch_barrier_async,我们只要按照前面所述的顺序将任务分配到队列就OK,剩下的都不用管了。dispatch_barrier_async的参数跟dispatch_async一模一样的。上代码:
dispatch_queue_t queue = dispatch_queue_create("thread", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"任务1"); }); dispatch_async(queue, ^{ NSLog(@"任务2"); }); dispatch_async(queue, ^{ NSLog(@"任务3"); }); dispatch_barrier_async(queue, ^{ for (int i = 0; i < 10; i++) { NSLog(@"任务0-%d",i); } NSLog(@"任务0完成"); }); dispatch_async(queue, ^{ NSLog(@"任务4"); }); dispatch_async(queue, ^{ NSLog(@"任务5"); }); dispatch_async(queue, ^{ NSLog(@"任务6"); });看看运行结果:
可以看出前三个任务和后三个任务都分别并发完成了,而且后3个任务必须要等到任务0完成才能进行。
关于GCD知识点和常用方法就到这里。GCD是iOS中非常重要的多线程技术,也是开发中最常见的多线程方案了。
4.NSOperation和NSOperationQueue
NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到 NSOperation 和 NSOperationQueue 分别对应 GCD 的 任务 和 队列 。操作步骤也很好理解:
1. 将要执行的任务封装到一个 NSOperation 对象中。
2. 将此任务添加到一个 NSOperationQueue 对象中。
然后系统就会自动在执行任务。
添加任务
值得说明的是,NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation 。创建一个 Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。
NSInvocationOperation : 需要传入一个方法名。
//1.创建NSInvocationOperation对象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2.开始执行 [operation start];
NSBlockOperation
//1.创建NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //2.开始任务 [operation start];
NSBlockOperation 有一个方法:addExecutionBlock: ,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务。这个方法必须在operation start 之前添加。
获取队列
主队列:[NSOperationQueue mainQueue]
其他队列:其他队列的任务会在其他线程并行执行。[[NSOperationQueue alloc] init]
将 NSOperationQueue 与 GCD的队列 相比较就会发现,这里没有串行队列,NSOperationQueue有个最大并发数的属性:maxConcurrentOperationCount,用来设置最多可以让多少个任务同时执行。当你把它设置为 1 的时候,NSOperationQueue就是串行了!和GCD类似,NSOperationQueue 还有一个添加任务的方法,- (void)addOperationWithBlock:(void (^)(void))block; ,这样就可以添加一个任务到队列中了,十分方便。
NSOperation 有一个非常实用的功能,那就是 添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:
//1.任务一:下载图片NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"下载图片 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0];}];//2.任务二:打水印NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"打水印 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0];}];//3.任务三:上传图片NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"上传图片 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0];}];//4.设置依赖[operation2 addDependency:operation1]; //任务二依赖任务一[operation3 addDependency:operation2]; //任务三依赖任务二//5.创建队列并加入任务NSOperationQueue *queue = [[NSOperationQueue alloc] init];[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
注意:不能添加相互依赖,会死锁,比如 A依赖B,B依赖A。可以使用 removeDependency 来解除依赖关系。可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没关系。
iOS中的多线程差不多就这样了。这只是对多线程做了简单的解释和概括,不过暂时也就先这样了。
- 谈一谈iOS中的多线程
- ios 开发中的多线程
- ios 开发中的多线程
- iOS中的多线程
- IOS中的多线程
- iOS 中的多线程总结
- iOS中的多线程
- IOS 中的多线程问题
- iOS开发中的多线程
- iOS中的多线程编程
- iOS中的多线程
- iOS中的多线程
- iOS中的多线程
- iOS中的GCD多线程
- iOS开发 中的 多线程
- IOS中的多线程
- iOS中的多线程使用
- 浅谈iOS中的多线程
- DAO和Service层的一些解释
- 欢迎使用CSDN-markdown编辑器
- Spring Data JPA
- 【boost】boost::asio(3)——socket编程
- kafka_consumer接口(旧版)
- 谈一谈iOS中的多线程
- Drawerlayout侧滑菜单
- MQTT--入门(二)
- Spring Boot 内嵌Tomcat的端口号的修改
- lucene7.1.0 (三) 索引的curd
- SpringBoot 基于swagger实现RESTfulApi风格
- 微信公众平台测试账号的注册与申请
- (二十四)Java设计模式之解释器模式
- 原生js支持的编码转换方法