iOS开发——多线程

时间:2016-03-09 20:43:36   收藏:0   阅读:349

很多朋友都说iOS开发中,最难理解和学习的就是多线程,很多的原理实现都是通过log看到,也比较抽象,本人也是在多线程方面投入过很多脑细胞。。无论这方面的知识掌握和应用起来是否轻松,牢固的基本功、正确的认识理解、再加上充分的实战经验,都能助你将其搞定。这里先介绍一些多线程的知识以及应用,作为讨论,大家共同学习。

一、多线程基本概念

1、线程与进程 

(1)进程:操作系统的每一个应用程序就是一个进程

(2)线程:进程的基本执行单元,一个进程的所有任务都在线程中执行

2、主线程

(1)定义:一个程序运行后,默认会开启1个线程,称为“主线程”或“UI线程”。其他为“子线程”。

(2)作用及注意:线程一般用来 刷新UI界面 ,处理UI事件(比如:点击、滚动、拖拽等事件),避免将耗时的操作放到主线程,以免造成主线程卡顿。

3、多线程原理

(1)是CPU快速的在多个线程之间的切换(自身的随机调度算法)。

(2)同步/异步:

4、iOS多线程实现方案

 技术分享

 

5、线程的占用空间:

(1)子线程:512KB。

(2)主线程:512KB。这里官方文档给出的是1M,实际测试为512,可以打印线程的stackSize属性验证。

6、线程的状态和生命周期:

(1)控制线程的状态(以NSThread管理线程为例)

  a、启动线程:- (void)start;

        线程进入就绪状态,当线程执行完毕后自动进入死亡状态。

  b、暂停(阻塞)线程

    + (void)sleepUntilDate:(NSDate *)date;

    + (void)sleepForTimeInterval:(NSTimeInterval)ti;

    线程进入阻塞状态

  c、停止线程

    + (void)exit;

    线程进入死亡状态

(2)状态图

 技术分享

 

7、线程的属性(以NSThread管理线程为例,一下是NSTread类中的方法或属性)

 (1)stackSize:占内存大小

 (2)name:名字

 (3)threadPriority:优先级(不推荐使用)

  (4)  qualityOfService:服务质量 

 

二、多线程深入理解

1、线程间资源共享/抢夺

 (1)定义:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,当多个线程访问同一块资源时,各个线程提取和修改数据不同步,很容易引发数据错乱和数据安全问题。

 (2)互斥锁(线程同步) :解决上面的问题

      线程执行到synchronized

    i.   检查锁状态 如果是开锁状态转到ii  ,如果上锁转到v 

    ii.   上锁(0)    

    iii.      执行代码块

    iv.    执行完毕 开锁(1)      

    v.       线程等待(就绪状态)

2、原子属性

(1)属性中的修饰符

3、线程安全

1)多个线程同时操作一个全局变量是不安全的,使用自旋锁并不是绝对的安全(因为单写多读)。

(2)线程安全:在多个线程进行读写操作时,仍然能够保证数据的正确 。使用互斥锁可以实现,但是消耗性能。

(3)关于主线程(UI线程):几乎所有UIKit??提供的类都是线程不安全的,所有更新UI的操作都在主线程上执行。

4、NSRunLoop

(1)功能作用:运行循环,又叫消息循环或事件循环。 

(2)特点:

(3)(两大核心之一)输入事件

(4)(两大核心之二)运行模式(消息循环模式):

 //定时源 (计时器)
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
        /*
        参数1:输入源
        参数2:输入源的模式,要和当前消息循环的模式对应,才可以让消息循环执行输入源
        NSDefaultRunLoopMode默认模式 NSRunLoopCommonModes包含了很多种模式
    */
    [[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

5、自动释放池

(1)主线程自动释放池的创建和销毁:

(2)子线程的自动释放池:

(3)什么时候使用自动释放池:(官方文档建议)

for (int i = 0; i < largeNumber; ++i) {
     @autoreleasepool {
        NSString *str = @"Hello World";
            str = [str stringByAppendingFormat:@" - %d", i];
            str = [str uppercaseString];

        }
    }

 

(4)示意图

技术分享

三、线程管理————pthread

1、一套通用的多线程API,纯C语言,操作难度大,在iOS开发中基本不使用。

2、基本使用方式

#import <pthread.h>
    //线程编号的地址,本质是结构体类型
    pthread_t pthread;
    //方法的返回值:0 成功, 其它失败

     int result =  pthread_create(&pthread, NULL, demo, NULL);

    /*
    pthread_create函数的参数介绍    

    第一个参数:  线程编号的地址;
    第二个参数:  线程的属性;
    第三个参数: 线程要执行的方法,其中void *(*)(void *)具体代表含义:
        //函数的返回值类型: void *,类似于oc中的id;
        //函数的名称:函数指针;
        //函数的参数:void *;
        第四个参数:线程要执行的方法的参数。
    
    */

 

四、线程管理————NSThread

1、创建新线程的三种方式,例如:

//方式一:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:nil]; [thread start];
//方式二: [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:nil];
//方式三:
准确的说此方法是NSObject的

[self performSelectorInBackground:@selector(demo:) withObject:nil];

2、NSThread在调试中的使用

五、线程管理————GCD

 在之前文章中,已经针对GCD的基本使用做了详细介绍,不在这里复述了。可参阅:http://www.cnblogs.com/cleven/p/5249246.html

本文只对GCD的其他操作进行一些补充。

1、延迟操作

实例:

//延时操作
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  });
        /*
        dispatch_after的参数
    参数1  dispatch_time_t when
    多少秒之后执行
    参数2  dispatch_queue_t queue
    任务添加到那个队列
    参数3  dispatch_block_t block
    要执行的任务
    */

 

2、一次性执行

(1)定义:程序运行中只执行一次。一次性执行是线程安全的,可以使用一次性执行创建单例对象,效率比互斥锁高。

(2)实现:可以用来创建单例对象。

    //原理:当onceToken为0时执行方法,然后将全局变量oneceToken更改为-1,以后就无法再执行。
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    //要执行一次的代码;
    });

3、调度组

(1)定义:有时候需要在多个异步任务都执行完成之后继续做某些事情,比如下载歌曲,等所有的歌曲都下载完毕之后转到主线程提示用户,这样需要一个顺序的统一调度。

(2)实现:

//1 全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //2 调度组
    dispatch_group_t group = dispatch_group_create();
    //3 添加任务
    //把任务添加到队列,等任务执行完成之后通知调度组,任务是异步执行
    dispatch_group_async(group, queue, ^{
        NSLog(@"歌曲1下载完毕  %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"歌曲2下载完毕  %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"歌曲3下载完毕  %@",[NSThread currentThread]);
    });
    //4 所有任务都执行完成后,获得通知 (异步执行)
    //等调度组中队列的任务完成后,把block添加到指定的队列
    dispatch_group_notify(group, queue, ^{
        NSLog(@"所有歌曲都已经下载完毕!   %@",[NSThread currentThread]);
    });
 
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //在主线程,更新UI控件,提示用户
        NSLog(@"播放器更新完毕!  %@",[NSThread currentThread]);
    });
    NSLog(@"over");

(3)原理:

//1 全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
 
    //2 调度组
    dispatch_group_t group = dispatch_group_create();
 
    //ARC中不用写
//    dispatch_retain(group);
    //3 进入调度组,执行此函数后,再添加的异步执行的block都会被group监听
    dispatch_group_enter(group);
    
    //4 添加任务一
    dispatch_async(queue, ^{
        NSLog(@“下载第一首歌曲!”);
        dispatch_group_leave(group);
        //ARC中此行不用写,也不能写
//        dispatch_release(group);
    });
    //5 添加任务二
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@“下载第二首歌曲”);
        dispatch_group_leave(group);
        //ARC中此行不用写,也不能写
        //dispatch_release(group);
    });
    
    //6  获得调度组的通知
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@“歌曲都已经下载完毕! %@",[NSThread currentThread]);
    });
//7 等待调度组 监听的队列中的所有任务全部执行完毕,才会执行后续代码,会阻塞线程(很少使用)
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

 

六、线程管理————NSOperation

1、NSOperation的作用以及特点

(1)NSOperation是OC语言中基于GCD的面向对象的封装,NSOperation是iOS2.0推出的,iOS4之后(GCD出现)重写了NSOperation。

(2)使用起来比GCD更加简单(面向对象)。同时,苹果推荐使用,使用NSOperation不用关心线程以及线程的生命周期。

(3)提供了一些用GCD不好实现的功能,比如暂停,取消,最大并发数、依赖关系。当然GCD也有自己的特有,比如延迟、一次性执行、调度组。

(4)NSOperation是抽象类,约束子类都具有共同的属性和方法,不能直接使用,而是使用其子类。

(5)任务是并发执行的,除非遇到主队列(start方法除外)。

2、NSOperationQueue 队列 

(1)两种队列

(2)NSOperationQueue的作用:

(3)无论是使用start还是加入队列的方式来执行操作,系统都会调用NSOperation中的main方法,所以如果自定义NSOperation,就要重写此方法。

(4)添加操作到队列(主队列也一样)

    - (void)addOperation:(NSOperation *)op;
    - (void)addOperationWithBlock:(void (^)(void))block;    

 (5) 其他一些常用方法和属性

 

3、NSOperation子类——NSInvocationOperation

(1)实例

//建NSInvocationOperation对象
    - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
//调用start方法开始执行操作,一旦执行操作,就会调用target的sel方法
    - (void)start;

(2)注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作,只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作。

 

4、NSOperation子类——NSBlockOperation

(1)实例:

//创建NSBlockOperation对象
    + (id)blockOperationWithBlock:(void (^)(void))block;
//通过addExecutionBlock:方法添加更多的操作
    - (void)addExecutionBlock:(void (^)(void))block;

(2)注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作。

    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    //操作添加额外的任务
    [op addExecutionBlock:^{
        NSLog(@"Execution %@",[NSThread currentThread]);
    }];
    [op start];
    
    //如果NSBlockOperation的操作数>1 开启新的线程
    //这时有两个任务并发执行,一个在主线程,一个在新开启的线程

 

5、并发数

(1)定义:同时执行的任务数,比如,同时执行3个任务放到3个线程,并发数就是3。

(2)最大并发数及相关方法:最大并发数是系统同一时间并发执行任务的最大数。系统可以开辟多个线程、队列可以拥有多个任务,但是同时执行的任务数只能是设定好的最大并发数,直到队列中任务执行完毕。

(3)执行的过程

  a、把操作添加到队列

  b、去线程池去取空闲的线程,如果没有就创建线程

  c、把操作交给从线程池中取出的线程执行

  d、执行完成后,把线程再放回线程池中

  e、重复b,c,d直到所有的操作都执行完

6、优先级

(1)方法一设置NSOperation在queue中的优先级,可以改变操作的执行优先级,注意这是NSOperation中的属性,已经不推荐使用了。

(2)方法二:iOS8以后推荐使用服务质量 qualityOfService属性,这是个枚举值。

(3)注意:优先级只是告诉系统:在CUP随机调度的情况下,请尽量优先调用优先级高的任务去执行,并不能绝对保证CPU全部执行优先级高的任务。

7、依赖关系

(1)定义:类似于GCD的串行队列,NSOperation之间可以设置依赖来保证执行顺序。

(2)实例 

 NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1 验证账号");
    }];
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2 扣费");
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op3 下载应用");
    }];
    //操作依赖 不能设置相互依赖
    [op2 addDependency:op1];
    [op3 addDependency:op2];
    //把操作添加到队列中 waitUntilFinished是否等待这句代码执行完毕再来执行下面的代码
    [self.queue addOperations:@[op2,op3] waitUntilFinished:NO];
    
    //不同的队列之间也可以设置依赖关系
    [[NSOperationQueue mainQueue]addOperation:op1];

8、监听操作的完成

 (1)类似于GCD的操作组,NSOperation也可以监听操作的完成,这是NSOperation中的方法:

   //创建操作op,任务就是循环打印输出
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            for (int i = 0; i < 20; i++) {
                 NSLog(@"我是op===》 %d",i);
             }
        }];
    //设置操作优先级 最高的服务质量
    op.qualityOfService = NSQualityOfServiceUserInteractive;
    //把操作添加到队列中
        [self.queue addOperation:op];    
    //监听op的完成
    [op setCompletionBlock:^{
        NSLog(@"op 执行完毕了! %@",[NSThread currentThread]);
    }];
    
    //创建操作op2
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i <20; i++) {
            NSLog(@"我是op2+++》 %d",i);
        }
    }];
    //设置操作优先级 最低的服务质量
    op2.qualityOfService = NSQualityOfServiceBackground;
    //把操作添加到队列中
    [self.queue addOperation:op2];
   
    //本例中加入了优先级的使用:op和op2两个任务,op要优先于op2被CPU调度,理论上也是会优先于op2执行完毕,当op执行完毕时,op任务的结束被监听到,输出“执行完毕”,此时op2还在执行中。
    

 

七、线程间通信的几种方式

 线程间的通讯,关键在于获得不同的线程或队列,然后在不同线程中执行任务,比如从子线程到主线程,再从主线程到子线程。

 1、获得当前线程(队列)的方式

 (1) NSThread管理线程:           [NSThread currentThread];

 (2) NSOperation管理线程:       [NSOperationQueue currentQueue];

 2、获得主线程(主队列)的方式

 (1) NSThread管理线程:           [NSThread mainThread];  主线程

 (2) NSOperation管理线程:       [NSOperationQueue mainQueue]; 主队列

(3)GCD管理线程:                  dispatch_get_main_queue(); 主队列

 3、在某线程中执行

(1)此方法是NSObject的扩展(NSThread.h中):  

(2)开启新线程执行:

(3)在某个线程中执行:

 

到此处,本文只是简单的将iOS中多线程的基本概念和使用做了简单介绍和整理,如有问题,还望多包含并指教,随后将更多讨论些在实际开发中的应用。

 

原文:http://www.cnblogs.com/cleven/p/5259416.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!