一、进程与线程

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. <Android 基础(二十九)> Fragment (2) ~ DialogFragment

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

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

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

  9. Bootstrap <基础二十九>面板(Panels)

    Bootstrap 面板(Panels).面板组件用于把 DOM 组件插入到一个盒子中.创建一个基本的面板,只需要向 <div> 元素添加 class .panel 和 class .pa ...

  10. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】原创教程连载导读【连载完成,共二十九章】

    前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例 ...

随机推荐

  1. webapi输入验证过滤器ValidationActionFilter

    public class validationActionFilter:ActionFilterAttribute { public override void OnActionExecuting(S ...

  2. 转:JQuery读写Cookie

    Cookies是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存,或是从客户端的硬盘读取数据的一种技术.当你浏览某网站时,你硬盘上会生产一个非常小的文本文件,它可以记录你的用户ID.密码.浏览过 ...

  3. C++之路进阶——codevs3287(货车运输)

    3287 货车运输 2013年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond   题目描述 Description A 国有 n ...

  4. QT学习篇:入门(1)

    第一个为管理界面: (1)安全库存的设置,包括序号.物品代码.物品类型.最大库存量.最小库存量.最大库存比率.最小库存比率: (2)计算频率设置,包括:实时,定时(单位分为:分钟.小时.天),来自gl ...

  5. 经典排序算法 - 基数排序Radix sort

    经典排序算法 - 基数排序Radix sort 原理类似桶排序,这里总是须要10个桶,多次使用 首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,临时忽视十位数 比如 待排序数组[ ...

  6. H5前端上传文件的几个解决方案

    目前,几个项目中用到了不同的方法,总结一下分享出来. 第一种,通过FormData来实现. 首先,添加input控件file. <input type="file" name ...

  7. 制作支持 BIOS+UEFI 的 U 盘 grub2+bootmgr 引导 + deepin_recovery + deepin_iso + win_pe

    网盘下载:https://pan.baidu.com/s/1c2GXPo0 U盘为 FAT32,MBR分区表 1.下载:U盘grub2+bootmgr引导2017.12.6.2.7z 2.解压到 U盘 ...

  8. Maven - &lt;Profile&gt;详解

    转载自:https://www.cnblogs.com/wxgblogs/p/6696229.html Profile能让你为一个特殊的环境自定义一个特殊的构建:profile使得不同环境间构建的可移 ...

  9. 卸载时候出现: windows installer 程序有问题。此安装需要的dll不能运行 的一个解决方法

    卸载时候出现: windows installer 程序有问题.此安装需要的dll不能运行 安装Your Uninstaller来卸载

  10. 机器视觉:SSD Single Shot MultiBox Detector

    今天介绍目标检测中非常著名的一个框架 SSD,与之前的 R-CNN 系列的不同,而且速度比 YOLO 更快. SSD 的核心思想是将不同尺度的 feature map 分成很多固定大小的 box,然后 ...