ios基础篇(二十九)—— 多线程(Thread、Cocoa operations和GCD)
一、进程与线程
1、进程
进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内;
如果我们把CPU比作一个工厂,那么进程就好比工厂的车间,一个工厂有好多个车间,每个车间都在进行不同的工作,它们之间是独立互不干扰的。
2、线程
线程是进程的基本执行单元,一个进程的所有任务都在线程中执行;一个进程要想执行任务,必须得有线程(每个进程至少要有1条线程);
线程就好比车间里的工人,一个车间里可以有好多工人(一个进程可以包括多个线程),他们协同完成一个任务;
二、多线程
多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径。
一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务,可以提高程序的执行效率;
原理:同一时间,CPU只能处理1条线程,只有1条线程在工作(执行);多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
优缺点:
多线程的优点:
(1)能适当提高程序的执行效率
(2)能适当提高资源利用率(CPU、内存利用率)
多线程的缺点:
(1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU在调度线程上的开销就越大
(2)程序设计更加复杂:比如线程之间的通信、多线程的数据共享
iOS中几种多线程实现:
1、Thread
2、Cocoa operations
3、GCD(Grand Central Dispatch)(iOS4 之后)
1、NSThread
(1)NSThread有两种创建方式:
- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;
+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument;
//实例方法
thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:nil];
//启动线程
[thread start];
//类方法
[NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:nil];
selector :线程执行的方法,selector只能有一个参数,且不能有返回值;
target :selector消息发送的对象;
object :传输给target的唯一参数,也可以是nil;
两种创建方式的不同:
类方法一调用就会立即创建一个线程来做事情;
实例方法要直到我们手动调用 start 启动线程时才会真正去创建线程;
(2)不显式创建线程的方法(间接创建):
利用NSObject的方法 performSelectorInBackground:withObject:来创建;
//隐含产生新线程
[myView performSelectorInBackground:@selector(Action:) withObject:nil];
(3)NSThread相关属性及方法:
@property (copy) NSString *name; // 获取/设置线程的名字
+ (NSThread *)currentThread; // 获取当前线程的线程对象
+ (void)sleepForTimeInterval:(NSTimeInterval)ti; // 线程休眠(秒)
+ (void)sleepUntilDate:(NSDate *)date; // 线程休眠,指定具体什么时间休眠
+ (void)exit; // 退出线程(线程对象销毁,销毁后就不能再次启动线程,否则程序会崩溃)
2、NSOperation
(1)NSInvocationOperation
NSInvocationOperation的创建:
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction) object:nil];//object可以带一个参数
//启动线程,默认是不启动
[operation start];
- (void)operationAction{}
参数和NSTread一样。
(2)NSBlockOperation
NSBlockOperation 是 NSOperation 类的另外一个系统预定义的子类,我们可以用它来封装一个或多个 block。
一般来说,有以下两个场景我们会优先使用 NSBlockOperation 类:
当我们在应用中已经使用了 Operation Queues 且不想创建 Dispatch Queues 时,NSBlockOperation 类可以为我们的应用提供一个面向对象的封装;
我们需要用到 Dispatch Queues 不具备的功能时,比如需要设置 operation 之间的依赖关系、使用 KVO 观察 operation 的状态变化等;
NSBlockOperation的创建:
我们可以使它并发执行,通过使用addExecutionBlock方法添加多个Block,这样就能使它在主线程和其它子线程中工作。
- (NSBlockOperation*)blockOperation{
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block1,mainThread:%@,currebtThread:%@",[NSThread mainThread],[NSThread currentThread]);
}];
[blockOperation addExecutionBlock:^{
NSLog(@"block2,mainThread:%@,currebtThread:%@",[NSThread mainThread],[NSThread currentThread]);
NSLog(@"Finish block2");
}];
[blockOperation addExecutionBlock:^{
NSLog(@"block3,mainThread:%@,currebtThread:%@",[NSThread mainThread],[NSThread currentThread]);
NSLog(@"Finish block3");
}];
return blockOperation;
}
(3)NSOperationQueue
一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的;也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的。
1、NSOperation方法及属性:
// 设置线程的最大并发数
@property NSInteger maxConcurrentOperationCount; // 线程完成后调用的Block
@property (copy) void (^completionBlock)(void); // 取消线程
- (void)cancel;
2、创建一个操作队列:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
3、添加NSOperation到NSOperationQueue中:
//添加一个operation
[operationQueue addOperation:operation]; //添加一组operation
NSArray *operations = [NSArray arrayWithObjects:@"",@"", nil];
[operationQueue addOperations:operations waitUntilFinished:NO]; //添加一个block形式的operation
[operationQueue addOperationWithBlock:^() {
NSLog(@"blockOperation:%@", [NSThread currentThread]);
}];
4、设置NSOperation的依赖对象
(1)当某个NSOperation对象依赖于其它NSOperation对象的完成时,就可以通过addDependency方法添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。通过removeDependency方法来删除依赖对象。
如下代码:operation1依赖operation2,意思为先执行operation2,operation2完成后继续执行operation1;
[operation2 addDependency:operation1];
删除依赖对象,删除后则不存在依赖关系;如下代码:
[operation2 removeDependency:operation1];
(2)没有设置依赖关系的情况下:(默认)
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第1次操作:%@", [NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第2次操作:%@", [NSThread currentThread]);
}];
[operationQueue addOperation:operation1];
[operationQueue addOperation:operation2];
打印:

由打印信息可以看出,默认是按顺序进行的;
(3)设置依赖关系的情况下:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第1次操作:%@", [NSThread currentThread]);
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"执行第2次操作:%@", [NSThread currentThread]);
}];
[operation1 addDependency:operation2];
[operationQueue addOperation:operation1];
[operationQueue addOperation:operation2];
打印:

可以看出程序先执行operation2,后执行operation1。
如果要解除依赖关系,则:
[operation1 removeDependency:operation2];
注意:在NSOperationQueue类中,我们可以使用cancelAllOperations方法取消所有的线程。这里需要注意一下,不是执行cancelAllOperations方法时就会马上取消,是等当前队列执行完,下面的队列不会再执行。
3、 GCD(Grand Central Dispatch)
GCD 是Apple公司开发的一种技术,异步执行任务的技术之一,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式;在Mac OS X 10.6和IOS 4.0之后开始支持GCD。
工作原理:让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务。
GCD中的FIFO(First In First Out)队列称为dispatch queue,它可以保证先进来的任务先得到执行;
Dispatch Queue分三种:
(1)Main queue:main dispatch queue 是一个全局可用的串行队列,在应用程序的主线程上执行任务。此队列的任务和应用程序的主循环(run loop)要执行的事件源交替执行。因为运行在应用程序的主线程,main queue经常用来作为应用程序的一个同步点。
(2)Serial quque: 又称private dispatch queue(私有调度队列),每次运行一个任务,可以添加多个,执行次序FIFO,一般用再对特定资源的同步访问上。我们可以根据需要创建任意数量的串行队列,每一个串行队列之间是并发的。
(3)Concurrent queue: 又称为global dispatch queue,可以并发地执行多个任务,但是执行完成的顺序是随机的.
使用方法:
(1)dispatch_async
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
async表明异步运行,除了async,还有sync(同步),delay(延时);
block代表的是你要做的事情;
queue则是你把任务交给谁来处理了;
dispatch_async 这个函数是异步的,这就意味着它会立即返回而不管block是否运行结束。因此,我们可以在block里运行各种耗时的操作(如网络请求) 而同时不会阻塞UI线程。
举个例子看看它的实际用法:(下载一张图片)
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:imageView];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
NSURL *url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/h%3D200/sign=5dafb2a3586034a836e2bf81fb1249d9/d31b0ef41bd5ad6e194e5f4885cb39dbb7fd3cd8.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [[UIImage alloc] initWithData:data];
if (data) {
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = image;
});
}
});
}
运行:

比起NSThread和NSOperation用法是不是简单多了。
系统给每一个应用程序提供了四个concurrent dispatch queues,这四个并发调度队列是全局的,它们只有优先级的不同;
dispatch_queue_t mainQ = dispatch_get_main_queue();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"group1");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"group2");
});
dispatch_group_async(group, queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"group3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"print");
});
打印:

(3)dispatch_barrier_async
dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行;
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"queue1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"queue2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:];
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:];
NSLog(@"queue3");
});
打印:

(4)dispatch_apply
执行某个代码片段几次。
dispatch_apply(2, globalQ, ^(size_t index) {
// 执行2次
});
(5)dispatch_suspend/dispatch_resume
ios基础篇(二十九)—— 多线程(Thread、Cocoa operations和GCD)的更多相关文章
- ioS基础篇(十九)——UIResponder简析
UIResponder类定义了对象相应和控制事件的接口,他是UIApplication.UIView的超类,这类的实例通常被称为应答对象. 一.Responder对象 在iOS系统中,能够响应并处理事 ...
- ios基础篇(十四)——UITableView(二)属性及基本用法
上一篇说了UITableView的重用机制,让我们对UITableView有了简单了解,下面说说UITableView的属性及常见方法. 一.属性 1.frame:设置控件的尺寸和大小 2.backg ...
- ios基础篇(十二)——UINavgationController的使用(三)ToolBar
UIToolBar存在于UINavigationController导航栏控制器中,而且默认被隐藏:设置UINavigationController的toolbarHidden属性可显示UIToolB ...
- ios基础篇(十六)——UIWebView的基本使用
UIWebView是内置的浏览器控件,可以用它来浏览网页.打开文档等.UIWebView是一个混合体,具体的功能控件内置的,实现一些基本的功能.UIWebView可以查看Html网页,pdf文件,do ...
- ios基础篇(十八)——Delegate 、NSNotification 和 KVO用法及其区别
一.Delegate Delegate本质是一种程序设计模型,iOS中使用Delegate主要用于两个页面之间的数据传递.iphone中常用@protocol和delegate的机制来实现接口的功能. ...
- iOS基础篇(十五)——UIScrollView的基本用法
滚动视图(UIScrollView)通常用于显示内容尺寸大于屏幕尺寸的视图. 一.基本属性 1.CGSize contentSize :设置UIScrollView的滚动范围 2.CGPoint co ...
- 机器学习实战基础(二十九):决策树(二)DecisionTreeClassifier与红酒数据集
DecisionTreeClassifier与红酒数据集 1 sklearn.tree.DecisionTreeClassifier class sklearn.tree.DecisionTreeCla ...
- <Android 基础(二十九)> Fragment (2) ~ DialogFragment
简介 上一篇简单的介绍了下Fragment的使用方法,这一篇主要看下DialogFragment. 在android 3.0时被引入.是一种特殊的Fragment,用于在Activity的内容之上展示 ...
- ios基础篇(十)——UINavgationController的使用(一)UIBarButtonItem的添加
UINavigationController又被成为导航控制器,继承自UIViewController,以栈的方式管理所控制的视图控制器,下面就详细说一下UINavigationController的 ...
- python编程基础之二十九
栈和队列: 栈:先进后出,其他没多少特别之处了,一般可以用列表模拟栈,也可以用双端队列,封死一端. 队列:先进先出,也可以用列表模拟,但是一般用库函数,需要导collections 包:主要是为了解决 ...
随机推荐
- 显示Class 'Think\Controller\FuController' not found和Call to a member function assign() on a non-object 的错误问题
Class 'Think\Controller\FuController' not found 错误位置 FILE: D:\wamp\www\tinkphp\Application\Come\Cont ...
- 2.用vs2015创建Dotnet Core的mvc项目
如果你正确安装DotNetCore.1.0.1-VS2015Tools.Preview2.0.2,将会看到 就是第二个选项 ASP.NET Core Web Application 选择第三个,第一次 ...
- jquery ajax异步请求
得先知道后台接口给ajax访问(接口URl和传入接口的参数及参数类型),知道访问之后返回的数据类型,有哪些数据. 选择异步请求的方式,常用的有三种,如$.ajax().$.post().$.get ...
- python 初级1
List:Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 构造list非常简单,按照上面的代码,直接用 [ ] 把list的所有元素都括起来, ...
- 使用Spring AsyncRestTemplate对象进行异步请求调用
直接上代码: package com.mlxs.common.server.asyncrest; import org.apache.log4j.Logger; import org.springfr ...
- Win10无法安装提示磁盘布局不受UEFI固件支持怎样解决
微软在推出Win10系统以后,就向Win7和Win8.1系统用户提供了免费升级Win10系统的推送,但是用户在安装Win10系统的时候,却有一部分用户反映,遇到提示“无法安装Windows,因为这台电 ...
- HTML5扩展之微数据与丰富网页摘要
一.微数据是? 一个页面的内容,例如人物.事件或评论不仅要给用户看,还要让机器可识别.而目前机器智能程度有限,要让其知会特定内容含义,我们需要使用规定的标签.属性名以及特定用法等.举个简单例子,我们使 ...
- 基于注解的Spring AOP的配置和使用
摘要: 基于注解的Spring AOP的配置和使用 AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.可以通过预编译方式和运行期动态代理实现在不 ...
- 关于C语言结构体,指针,声明的详细讲解。——Arvin
关于结构体的详细分析 只定义结构体 struct Student { int age; char* name; char sex;//结构体成员 };//(不要忘记分号) Student是结构体的名字 ...
- js生成二维码
jquery.qrcode.js 使用 1. 加载 jQuery 和 jquery.qrcode.js: <script type='text/javascript' src='http://c ...