一、进程与线程

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. ios基础篇(十)——UINavgationController的使用(一)UIBarButtonItem的添加

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

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

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

  9. WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载]

    原文:WCF技术剖析之二十九:换种不同的方式调用WCF服务[提供源代码下载] 我们有两种典型的WCF调用方式:通过SvcUtil.exe(或者添加Web引用)导入发布的服务元数据生成服务代理相关的代码 ...

  10. JAVA之旅(二十九)——文件递归,File结束练习,Properties,Properties存取配置文件,load,Properties的小练习

    JAVA之旅(二十九)--文件递归,File结束练习,Properties,Properties存取配置文件,load,Properties的小练习 我们继续学习File 一.文件递归 我们可以来实现 ...

随机推荐

  1. 同一行多个div宽度自适应布局

    主要运用到的是:布局神器display:table-cell 元素两端对齐 第一个案例是让两个元素分别向左和向右对齐,如果是过去,我一定会用float来实现,但其实用table可以这么做: 自动平均划 ...

  2. jws.mono脚本安装详解

    就在最近两天,最新版本的jws.mono上线了,这个版本除了提供与之前版本拥有的功能外,还额外提供了一个“自动化”的安装脚本,通过执行该脚本,jws.mono将自动快速的安装到指定的目录,同时,通过改 ...

  3. &lt;dependency&gt;

      <dependency>             <groupId>org.hibernate</groupId>                       ...

  4. 基于Metronic的Bootstrap开发框架经验总结(5)--Bootstrap文件上传插件File Input的使用

    Bootstrap文件上传插件File Input是一个不错的文件上传控件,但是搜索使用到的案例不多,使用的时候,也是一步一个脚印一样摸着石头过河,这个控件在界面呈现上,叫我之前使用过的Uploadi ...

  5. Cassandra 键空间(keyspace),表(table)

    查看用户下信息: describe cluster; desc cluster;   查看所有keyspace: describe keyspaces; desc keyspaces;   查看key ...

  6. Python【8】-分析json文件

    一.本节用到的基础知识 1.逐行读取文件 for line in open('E:\Demo\python\json.txt'): print line 2.解析json字符串 Python中有一些内 ...

  7. JTA和JDBC事务

    一般情况下,J2EE应用服务器支持JDBC事务.JTA事务.容器管理事务.这里讨论JTA和JDBC事务的区别.这2个是常用的DAO模式事务界定方式.JDBC 事务 JDBC 事务是用 Connecti ...

  8. iOS 推送通知处理

    //这是程序杀死后再通过点击通知进入时调用的方法 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOpti ...

  9. 在WPF中使用字体图标

    一.源码描述    这是一款基于WPF窗体应用程序的字体图标示例源码,    该源码简单易懂使用于初学者和实战项目应用,    感兴趣的朋友们可以下载看看哦. 二.功能介绍    1.用ICO字体代替 ...

  10. cocos2dx 3.x(加载cocostudio进度条)

    // // MyLoagingScene.hpp // My // // Created by work on 16/10/13. // // #ifndef MyLoagingScene_hpp # ...