美文网首页
iOS开发网络多线程之多线程

iOS开发网络多线程之多线程

作者: 我是一只攻城狮_ifYou | 来源:发表于2016-06-01 19:02 被阅读294次

    iOS开发笔记-多线程01

    理论部分:

    一.进程

    1)概念:是指在系统中正在运行的一个应用程序,每个进程的存储空间是独立的(终端中输入top可以查看进程,按Q退出)

    P.S.进程与应用程序的差别:进程是有状态的,正在执行的

    2)进程是CPU进行资源分配与调度的基本单位

    二.线程

    1)线程是CPU调度的基本单位(1个进程想执行任务,必须要有线程 ),一个进程中所有的任务都在线程中执行

    2)一个线程中的任务的执行是串行的

    P.S.进程与线程的辨析

    1)一个程序可以对应多个进程,一个进程中可以有多个线程,但至少要有一个线程,线程是进程的一条路径

    2)同一个进程内的线程共享进程的资源

    3)进程可以分配资源,而线程不可以

    3)多线程并发执行:其实是CPU快速地在多个线程之间调度(只是假象,因为同一时间,CPU只能处理一条线程),建议开辟线程数量为3-5条

    4)主线程:也称为UI线程,作用是:1.显示\刷新UI界面 2.处理UI事件 (所有和UI相关的操作都需要在主线程中执行)

    打印时,若显示的number等于1,则为主线程,其他数字,则为子线程

    P.S.主线程注意点

    1.不要将耗时的任务放在主线程

    2.UI刷新操作要放在主线程中

    3.耗时的操作放在子线程中(后台线程)

    三.pthread

    
    说明:pthread的基本使用(需要包含头文件)
        //使用pthread创建线程对象
        pthread_t thread;
        //使用pthread创建线程
        //第一个参数:线程对象地址
        //第二个参数:线程属性
        //第三个参数:指向函数的指针
        //第四个参数:传递给该函数的参数
        pthread_create(&thread, NULL, run, NULL);
    
    

    四.NSThread

    1.一个NSThread代表一条线程

    2.优先级高,cpu调用的概率高(影响的也是CPU调度到的概率)

    
    1)NSThread创建线程的四种方式
            //第一种创建线程的方式:alloc initWithTarget.
            //特点:需要手动开启线程,可以拿到线程对象进行详细设置
                //创建线程
                /*
                 第一个参数:目标对象
                 第二个参数:选择器,线程启动要调用哪个方法
                 第三个参数:前面方法要接收的参数(最多只能接收一个参数,没有则传nil)
                 */
                NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"hmx"];
                 //启动线程
                [thread start];
    
            //第二种创建线程的方式:分离出一条子线程
            //特点:自动启动线程,无法对线程进行更详细的设置
                /*
                 第一个参数:线程启动调用的方法
                 第二个参数:目标对象
                 第三个参数:传递给调用方法的参数
                 */
                [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分离出来的子线程"];
    
            //第三种创建线程的方式:后台线程
            //特点:自动启动线程,无法进行更详细设置
                [self performSelectorInBackground:@selector(run:) withObject:@"我是后台线程"];
            
            //第四种创建线程方法:alloc init
            //新建一个类 继承自NSThread,重写内部的main方法来封装任务
                NSThread *thread = [[NSThread alloc]init];
                //启动线程
                [thread start];
    
        2)设置线程的属性
            //设置线程的名称
            thread.name = @"线程A";
    
            //设置线程的优先级,注意线程优先级的取值范围为0.0~1.0之间,1.0表示线程的优先级最高,如果不设置该值,那么理想状态下默认为0.5
            thread.threadPriority = 1.0;
    
    

    3.线程状态相关问题:

    1.线程的各种状态:新建-就绪-运行-阻塞-死亡
    2.当线程的任务执行完毕之后就销毁了
    3.当线程放入可调度线程池中,CPU才会调度
    4.线程已经死了,是不能再重新打开的
    5.当线程解除阻塞状态时,会进入就绪状态,而不是运行状态
    

    4.线程安全相关问题:

        1. 互斥锁:@synchronized(self){//需要锁定的代码}(推荐使用self)
        2. 前提条件:多个线程可能会访问同一块资源
        3. 注意点:1)要注意加锁的位置
                 2)锁对象必须是对象,且全局唯一
                 3)加上互斥锁之后,就会使线程同步(即线程永远都是按一个顺序调用,例:一开始是2->1->3的顺序,之后依旧为2->1->3的顺序)
                 4)线程是需要消耗性能的
        4. 专业术语-线程同步
        5. 原子和非原子属性(是否对setter方法加锁)
           atomic 线程安全,需要消耗大量资源
           nonatomic 非线程安全,适合内存小的移动设备  (开发中声明为这个)
    

    5.计算代码段间的执行时间

    
            //第一种方法
                NSDate *start = [NSDate date];
                //2.根据url地址下载图片数据到本地(二进制数据)
                NSData *data = [NSData dataWithContentsOfURL:url];
    
                NSDate *end = [NSDate date];
                NSLog(@"第二步操作花费的时间为%f",[end timeIntervalSinceDate:start]);
    
            //第二种方法
                CFTimeInterval start = CFAbsoluteTimeGetCurrent();
                NSData *data = [NSData dataWithContentsOfURL:url];
    
                CFTimeInterval end = CFAbsoluteTimeGetCurrent();
                NSLog(@"第二步操作花费的时间为%f",end - start);
    
    

    6.回到主线程刷新UI

        //4.1 第一种方式
            [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
        //4.2 第二种方式
            [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
        //4.3 第三种方式
            [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
        }
    

    五.PCD

    1.GCD基本知识

    1) 两个核心概念:队列和任务:
        队列:用来存放任务(决定在哪个线程执行任务)
        任务:执行什么操作
    2) 同步函数和异步函数的区别:
        同步:1.只能在当前线程中执行,不具备开启新线程的能力
            2.执行任务的方式:当执行到我时必须等我执行完才能执行后面的任务
        异步:1.可以在新的线程中执行任务,具备开启新线程的能力
            2.执行任务的方式:可以不用等我执行完毕,就可以直接执行后面的任务
    3)  串行队列:1.取出一个任务后,等到该任务执行完毕之后,接着去第二个任务
                2.创建方式:a.自己创建  b.主队列
        并行队列:1.取出一个任务后,接着执行第二个任务
                2.创建方式:a.自己创建  b.全局并发队列
        主队列:1.在安排任务的时候,会先检查主线程的状态,如果主线程忙,那么久暂停调度直到空闲为止
              2.想要实现控制系统开几条线程时,只需要控制创建几个队列
              3.凡是放在主队列里的任务都在主线程完成
    

    2.GCD基本使用【重要】

    01 异步函数+并发队列:开启多条线程,并发执行任务
    02 异步函数+串行队列:开启一条线程,串行执行任务
    03 同步函数+并发队列:不开线程,串行执行任务
    04 同步函数+串行队列:不开线程,串行执行任务
    05 异步函数+主队列:不开线程,在主线程中串行执行任务
    06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)
       P.S若当前是子线程执行的同步函数加上主队列的方式,不会发生死锁
    07 注意同步函数和异步函数在执行顺序上面的差异
    

    3.GCD的线程间通信:

    
            //0.获取一个全局的队列
            dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
            //1.先开启一个线程,把下载图片的操作放在子线程中处理
            dispatch_async(queue, ^{
    
            //2.下载图片
                NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
                NSData *data = [NSData dataWithContentsOfURL:url];
                UIImage *image = [UIImage imageWithData:data];
    
                NSLog(@"下载操作所在的线程--%@",[NSThread currentThread]);
    
            //3.回到主线程刷新UI
                dispatch_async(dispatch_get_main_queue(), ^{
                   self.imageView.image = image;
                   //打印查看当前线程
                    NSLog(@"刷新UI---%@",[NSThread currentThread]);
                });
    
            });
    
    

    4.GCD相关注意点

    1. 开线程的两个条件:1.必须是异步函数 2.必须不是主队列(主线程)
    2. 放到主队列里的任务,必须要在主线程中执行(不一定在当前线程中执行,即即使在子线程中调用了主队列,还是子主线程中调用)
    

    5.GCD常用函数:

       
       1)栅栏函数(控制任务的执行顺序)
            dispatch_barrier_async(queue, ^{
                NSLog(@"--dispatch_barrier_async-");
            });
        /*
        P.S 注意点:1.栅栏函数可以控制线程的执行顺序
                  2.栅栏函数不能使用全局并发队列
                  3.栅栏函数在执行时是独占的
                  4.dispatch_barrier_async == dispatch_async  这两个是等同的
        */
        
        2)延迟执行(延迟·控制在哪个线程执行)
              dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                NSLog(@"---%@",[NSThread currentThread]);
            });
        /*
        Q:GCD的延迟执行 是先等2秒再提交 OR 先提交再等2秒?
        A:先等2秒再提交(延迟提交)  因为任务提交到队列里就不好控制了
        */
    
        3)一次性代码(注意不能放到懒加载)
            -(void)once
            {
                //整个程序运行过程中只会执行一次
                //onceToken用来记录该部分的代码是否被执行过
                static dispatch_once_t onceToken;
                dispatch_once(&onceToken, ^{
    
                    NSLog(@"-----");
                });
            }
        /*
        一次性代码的特点:1.整个程序运行过程中只会执行一次
                          2.它本身是线程安全的
        */
    
        4)快速迭代(开多个线程并发完成迭代操作)
               dispatch_apply(subpaths.count, queue, ^(size_t index) {
            });
         /*
         快速迭代:多个线程(子线程与主线程一起工作的)一起并发执行任务的--->对顺序没有要求时使用
         Q:快速迭代若不是从0开始,怎么处理
         A:初始值若不是0,则不需要使用GCD的快速迭代,使用for循环即可
         */
        
        5)队列组(同栅栏函数)--->调度组
            //创建队列组
            dispatch_group_t group = dispatch_group_create();
            //队列组中的任务执行完毕之后,执行该函数
            dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);//这个方法本身也是异步的
    
        /*
        方便管理一个组内的操作
        */
    
        6)进入群组和离开群组
            dispatch_group_enter(group);//执行该函数后,后面异步执行的block会被gruop监听
            dispatch_group_leave(group);//异步block中,所有的任务都执行完毕,最后离开群组
           /*
           注意:dispatch_group_enter|dispatch_group_leave必须成对使用
                当需要监听多个任务时,则重复写一组即可
           */
           //死等方法:知道队列组中所有都执行完毕之后才过掉该方法(同步)
           //diepatch_group_wait(group,DISPATCH_TIME_FOREVER)
    

    相关文章

      网友评论

          本文标题:iOS开发网络多线程之多线程

          本文链接:https://www.haomeiwen.com/subject/rtihdttx.html