一、进程与线程

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线程。

//默认优先级的Global Dispatch Queue中执行Block
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     //可并行执行的处理
     //在Main Dispatch Queue中执行Block
     dispatch_async(dispatch_get_main_queue(), ^{
          //只能在主线程中执行的处理
          });
     });

举个例子看看它的实际用法:(下载一张图片)

- (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,这四个并发调度队列是全局的,它们只有优先级的不同;

//Global Dispatch Queue 默认优先级的获取方法
dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//Global Dispatch Queue高优先级的获取方法
dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//Global Dispatch Queue 低优先级的获取方法
dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//Global Dispatch Queue 后台优先级的获取方法
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
 
因为是全局的,我们不需要去创建。我们只需要通过使用函数dispath_get_global_queue去得到队列;
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   
dispatch_queue_t mainQ = dispatch_get_main_queue();   
 
(2)dispatch_group_async
dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。
 
监视Dispatch Queue处理执行的结束。
dispatch_group_create();
dispatch_group_async();
dispatch_group_notify();
dispatch_group_wait();
 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

当追加大量处理到Dispatch Queue时, 在追加处理的过程中,有时希望不执行已追加的处理。  此时,我们需要挂起Dispatch Queue。当可以执行时再恢复。
suspend是挂起,resume是恢复。
 
(6)dispatch_once

保证应用程序执行中只执行一次指定处理的API。
 
(7)Dispatch I/O
一次使用多线程更快地并列读取文件。
通过Dispatch I/O读写文件时,使用Global Dispatch Queue将一个文件按某个大小read/write。
也可以将文件分割为一块一块地进行读取处理,分割读取的数据通过使用Dispatch Data可以更为简单地进行结合和分割 。
 dispatch_io_create  生成Dispatch IO, 指定发生错误时用来执行处理的Block,以及执行该Block的Dispatch Queue。
 dispatch_io_set_low_water函数 设定一次读取的大小(分割的大小),
 dispatch_io_read函数使用Global Dispatch Queue开始并列读取。

ios基础篇(二十九)—— 多线程(Thread、Cocoa operations和GCD)的更多相关文章

  1. ioS基础篇(十九)——UIResponder简析

    UIResponder类定义了对象相应和控制事件的接口,他是UIApplication.UIView的超类,这类的实例通常被称为应答对象. 一.Responder对象 在iOS系统中,能够响应并处理事 ...

  2. ios基础篇(十四)——UITableView(二)属性及基本用法

    上一篇说了UITableView的重用机制,让我们对UITableView有了简单了解,下面说说UITableView的属性及常见方法. 一.属性 1.frame:设置控件的尺寸和大小 2.backg ...

  3. ios基础篇(十二)——UINavgationController的使用(三)ToolBar

    UIToolBar存在于UINavigationController导航栏控制器中,而且默认被隐藏:设置UINavigationController的toolbarHidden属性可显示UIToolB ...

  4. ios基础篇(十六)——UIWebView的基本使用

    UIWebView是内置的浏览器控件,可以用它来浏览网页.打开文档等.UIWebView是一个混合体,具体的功能控件内置的,实现一些基本的功能.UIWebView可以查看Html网页,pdf文件,do ...

  5. ios基础篇(十八)——Delegate 、NSNotification 和 KVO用法及其区别

    一.Delegate Delegate本质是一种程序设计模型,iOS中使用Delegate主要用于两个页面之间的数据传递.iphone中常用@protocol和delegate的机制来实现接口的功能. ...

  6. iOS基础篇(十五)——UIScrollView的基本用法

    滚动视图(UIScrollView)通常用于显示内容尺寸大于屏幕尺寸的视图. 一.基本属性 1.CGSize contentSize :设置UIScrollView的滚动范围 2.CGPoint co ...

  7. 机器学习实战基础(二十九):决策树(二)DecisionTreeClassifier与红酒数据集

    DecisionTreeClassifier与红酒数据集 1 sklearn.tree.DecisionTreeClassifier class sklearn.tree.DecisionTreeCla ...

  8. <Android 基础(二十九)> Fragment (2) ~ DialogFragment

    简介 上一篇简单的介绍了下Fragment的使用方法,这一篇主要看下DialogFragment. 在android 3.0时被引入.是一种特殊的Fragment,用于在Activity的内容之上展示 ...

  9. ios基础篇(十)——UINavgationController的使用(一)UIBarButtonItem的添加

    UINavigationController又被成为导航控制器,继承自UIViewController,以栈的方式管理所控制的视图控制器,下面就详细说一下UINavigationController的 ...

  10. python编程基础之二十九

    栈和队列: 栈:先进后出,其他没多少特别之处了,一般可以用列表模拟栈,也可以用双端队列,封死一端. 队列:先进先出,也可以用列表模拟,但是一般用库函数,需要导collections 包:主要是为了解决 ...

随机推荐

  1. YTU 3027: 哈夫曼编码

    原文链接:https://www.dreamwings.cn/ytu3027/2899.html 3027: 哈夫曼编码 时间限制: 1 Sec  内存限制: 128 MB 提交: 2  解决: 2 ...

  2. bzoj1007[HNOI2008]水平可见直线

    cycleke神说要用半平面交(其实他也用的凸包),把我吓了一跳,后来发现(看题解)其实可以先按斜率排序,再将最小的两条线入栈,如果其与栈顶元素的交点在上一个点的左边,则将栈顶元素出栈.这是一个开口向 ...

  3. tomcat 配置客户端证书认证

    在完成配置客户端证书认证后,浏览器以https访问服务器的时候,会提示选择证书,之后,服务器端会验证证书.也就意味着只有拥有有效证书的客户端才能打开该网站. 以下是具体的配置过程. 1. 在服务器端生 ...

  4. 2016年12月29日 星期四 --出埃及记 Exodus 21:24

    2016年12月29日 星期四 --出埃及记 Exodus 21:24 eye for eye, tooth for tooth, hand for hand, foot for foot,以眼还眼, ...

  5. Dream It Possible

    反复听着Dream It Possible,想起自己的华为岁月,百感交集!

  6. VirtualBox 共享文件夾

    説明:host為window10,guest為centos7 一.安装VBoxLinuxAdditions 1. 在guest上挂载virtualbox安装目录下的VBoxGuestAdditions ...

  7. 微信小程序-登陆、支付、模板消息

    wx.login(OBJECT) 调用接口获取登录凭证(code)进而换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key).用户数据的加解密通讯需要 ...

  8. 修改shell提示符的显示格式

    默认的提示符显示方式如下: [root@localhost foldername]# 由于太长,希望能缩短到: [foldername]# 修改方式: # vim /etc/bashrc 找到第41行 ...

  9. 21.TFS文件系统搭建笔记

    TFS文件系统搭建笔记 参考地址: https://github.com/alibaba/tfs/blob/master/INSTALL.md https://github.com/alibaba/t ...

  10. jquery总结06-动画事件03-淡入淡出效果

    .fadeout()淡出 .fadein()淡入 .fadeTaggle()淡入淡出切换 .fadeTo()淡入设定透明度 淡入淡出fadeIn与fadeOut都是修改元素样式的opacity属性,但 ...