iOS开发iOS Developer

iOS开发之多线程

2018-05-05  本文已影响58人  hmj1993

进程

1.进程是指在系统中正在运行的一个应用程序
2.每个进程之间是相互独立的,每个进程均运行在其专用且受保护的内存空间中

线程

1.一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程)
2.一个进程(程序)的所有任务都在线程中执行

线程的串行

1.一个线程中任务的执行是串行的
2.如果要在一个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务
3.也就是说,在同一时间内,一个线程只能执行一个任务
4.因此,也可以认为线程是进程中的一条执行路径

进程和线程的比较

1.线程是CPU调用(执行任务)的最小单位
2.进程是CPU分配资源和调度的单位
3.一个程序可以对应多个进程,一个进程中可以有多个线程,但至少要有一个线程
4.同一个进程内的线程共享进程的资源

多线程

1.一个进程中可以开启多条线程,每个线程可以并发(同时)执行不同的任务
2.多线程可以提高任务的执行效率

多线程的原理

1.同一时间,CPU只能处理一条线程,只有一条线程在执行(单核)
2.多线程并发(同时)执行,其实是CPU快速的在多条线程之间调度(切换)
3.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
4.如果线程非常多,会导致CPU在很多的线程之间调度,消耗大量的CPU资源,每条线程被调度执行的频次会降低(线程的执行效率降低)

多线程的优缺点

多线程在iOS开发中的应用

技术方案 简介 语言 线程生命周期 使用频率
pthread 一套通用的多线程API;
适用于unix/linux/windows等系统;
跨平台/可移植;
使用难度大
C 程序员管理 几乎不用
NSThread 使用更加面向对象;
简单易用,可直接操作线程对象
OC 程序员管理 偶尔使用
GCD 旨在替代NSThread 等线程技术;
充分利用设备的多核
C 自动管理 经常使用
NSOperation 基于GCD(底层是GCD);
比GCD 多了一些更简单实用的功能;
使用更加面向对象
OC 自动管理 经常使用

pthread的简单使用

1.#import <pthread.h>
2. //创建线程对象
    pthread_t thread;
    //创建线程
    /*
     第一个参数:线程对象 传递地址
     第二个参数:线程的属性 可设为null
     第三个参数:指向函数的指针
     第四个参数:函数需要接受的参数
     */
    pthread_create(&thread, NULL, test, NULL);

void *test(void * param){ 
    return NULL;
}

NSThread的基本使用

    /*
     第一个参数:目标对象
     第二个参数:方法选择器 调用的方法
     第三个参数:前面调用方法需要传递的参数 可以为nil
     */
    NSThread *subthread=[[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"123"];
    //设置线程的名字
    subthread.name=@"subthread";
    //优先级  0.0(最低)--0.5(默认)--1.0( 最高)
    subthread.threadPriority=1;
    //需要手动启动线程
    [subthread start];

-(void)run:(NSString *)param{
    
}
//分离子线程,自动启动线程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"分离子线程"];
//开启一条后台线程
[self performSelectorInBackground:@selector(run:) withObject:@"开启一条后台线程"];
@synchronized(锁对象,通常为self){
//需要锁定的代码
}

锁:必须是全局唯一的一把锁,
锁定一份代码只用一把锁,用多把锁是无效的
1.注意加锁的位置
2.注意加锁的前提条件,多线程共享同一块资源
3.注意加锁是需要付出代价的,需要耗费性能
4.加锁的结果:线程同步
线程同步:多条线程在同一条线上执行(按顺序的执行任务)

-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
-(void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

GCD

纯c语言,提供了非常多强大的函数

//1.创建队列
    /*
     第一个参数:c语言的字符串,就是一个标识符,用来区分队列,没有实际意义
     第二个参数:队列类型
     DISPATCH_QUEUE_CONCURRENT 并行队列
     DISPATCH_QUEUE_SERIAL 串行队列
     */
   dispatch_queue_t queue= dispatch_queue_create("123", DISPATCH_QUEUE_CONCURRENT);//方法一
    /*
     第一个参数:队列优先级
     第二个参数:暂时无用,写0即可
     */
    dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//方法二,获得全局并发队列
    
  //2.封装任务,添加任务到队列中
    /*
     第一个参数:队列
     第二个参数:要执行的任务
     */
    dispatch_async(queue, ^{
        
    });
  并发队列 串行队列 主队列
异步函数 会开启多条线程,队列中的任务是并发执行 会开启线程,开一条线程,队列中的任务是串行执行 不会开启线程,所有任务都在主线程中执行,队列中的任务是串行执行
同步函数 不会开启线程,队列中的任务是串行执行 不会开启线程,队列中的任务是串行执行 在主线程中会产生死锁,在子线程中没有影响
dispatch_queue_t queue= dispatch_queue_create("123", DISPATCH_QUEUE_CONCURRENT);

2.获得全局并发队列

dispatch_queue_t queue1=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue= dispatch_queue_create("123", DISPATCH_QUEUE_SERIAL);

2.获得主队列(和主线程相关的队列)

dispatch_queue_t queue1=dispatch_get_main_queue();
  1. 延迟执行
    /*
     第一个参数:DISPATCH_TIME_NOW 从现在开始计算时间
     第二个参数:延迟的时间 gcd时间单位:纳秒
     第三个参数:队列
     */
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
    });
  1. 一次性代码,不能放在懒加载中,主要用在单例中
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       
   });
  1. gcd栅栏函数
    3.1 作用:控制多线程中并发任务的执行顺序,比如放置在任务1,任务2之后,任务3之前,则结果是等任务1,2都执行完毕后,再执行任务3
    3.2 注意:栅栏函数不能使用全局并发队列
    /*
     第一个参数:队列
     第二个参数:操作
     */
    dispatch_barrier_async(queue, ^{
        //操作
    });
  1. gcd快速迭代(遍历)
    for循环是同步的,gcd是开子线程和主线程一起完成遍历任务,任务的执行是并发的
    /*
     第一个参数:遍历的次数
     第二个参数:队列(只能传并发队列,串行队列无效果,主队列会死锁)
     第三个参数:index 索引
     */
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSLog(@"%zd",index);
    });
  1. gcd队列组
    gcd队列组的使用
    dispatch_queue_t queue2=dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group=dispatch_group_create();
    /*
     作用:
     第一个:封装任务
     第二个:把任务添加到队列中
     第三个:会监听任务的执行情况,通知group
     */
    dispatch_group_async(group, queue2, ^{
        
    });
    dispatch_group_async(group, queue2, ^{
        
    });
    dispatch_group_async(group, queue2, ^{
        
    });
    //拦截通知,当队列组中所有的任务都执行完毕的时候进入到下面的方法
    //内部本身是异步的
    dispatch_group_notify(group, queue2, ^{
        
    });

老式写法

    dispatch_queue_t queue3=dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group1=dispatch_group_create();
    //在该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
    //enter和leave必须配套出现
    dispatch_group_enter(group1);
    dispatch_sync(queue3, ^{
        //离开群组
        dispatch_group_leave(group1);
    });
    
    dispatch_group_enter(group1);
    dispatch_sync(queue3, ^{
        //离开群组
        dispatch_group_leave(group1);
    });
    
    //等待 等价于dispatch_group_notify,本身是阻塞的,即我不执行,下面的也不执行
    //DISPATCH_TIME_FOREVER 表示死等,直到队列组中所有的任务都执行完毕之后才执行,
    dispatch_group_wait(group1, DISPATCH_TIME_FOREVER);

NSOperation

//1.创建操作,封装任务
    /*
     第一个参数:目标对象
     第二个:调用的方法
     第三个:调用的方法的参数
     */
    NSInvocationOperation *op1=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];

2.NSBlockOperation

//1.创建操作,封装任务
NSBlockOperation *op2=[NSBlockOperation blockOperationWithBlock:^{
        //任务
    }];
//追加任务
    //注意:如果一个操作中的任务数量大于1,那么会开子线程并发执行任务
    //注意:不一定是子线程,有可能是主线程
    [op2 addExecutionBlock:^{
        //任务
    }];

3.自定义子类继承NSOperation,实现内部的main方法

//告知要执行的任务是什么
//有利于代码隐蔽,有益于代码复用
-(void)main{
   //任务
}

创建好操作后

//2.启动/执行操作
//注意:如果直接start,那么不会创建新线程,就和一般的[self 方法名];效果一样,无法实现多线程编程
    [op1 start];
    [op2 start];
//2.创建队列,这样才可能实现多线程编程
    /*
     主队列:[NSOperationQueue mainQueue]和gcd中的主队列一样,是串行队列,在主线程中执行
     非主队列:[[NSOperationQueue alloc]init]非常特殊,因为同时具备并发和串行的功能
     默认情况下,非主队列是一个并发队列
     */
    
    NSOperationQueue *queue=[[NSOperationQueue alloc]init];
    //3. 添加操作到队列中,内部已经调用了start方法
    [queue addOperation:op2];
    
    //简便方法   首先创建了NSBlockOperation操作,然后把操作添加到队列里
    [queue addOperationWithBlock:^{
        
    }];

NSOperation的其他用法

NSOperationQueue *queue1=[[NSOperationQueue alloc]init];
    //设置最大并发数:同一时间最多有多少个操作/任务可以执行
    //设置为1则为串行队列,但是不等于只开一条线程
    //不能设置为0,因为会不执行任务
    //大于1就是并发队列
    //设置为-1则代表最大值,不受限制
    queue.maxConcurrentOperationCount=5;
    //暂停 可以恢复 不能暂停当前正在执行的任务,需要等当前任务执行完毕再暂停
    //队列中的任务也是有状态的  已经执行完毕/正在执行/排队等待执行
    [queue setSuspended:YES];
    //继续
    [queue setSuspended:NO];
    //取消  不可以恢复 不能暂停当前正在执行的任务,需要等当前任务执行完毕再暂停
    //该方法内部调用了所有操作的cancel方法
    //自定义NSOperation
    gh *op3=[[gh alloc]init];
//需要先在自定义的gh中的main方法中设置
    if (op3.isCancelled==YES) {
        return;
    }
    [queue cancelAllOperations];

NSOperation操作依赖和监听

   //添加操作依赖
    //注意:不能循环依赖,不会崩溃,但是谁都不会执行  可以跨队列依赖
    [op1 addDependency:op2];
    [op2 addDependency:op3];
    
    //操作监听
    op3.completionBlock = ^{
        //操作
    };

NSOperation实现线程间通信

NSOperationQueue *queue3=[[NSOperationQueue alloc]init];
 NSBlockOperation *down=[NSBlockOperation blockOperationWithBlock:^{
        
        //操作
        
        //在主线程中更新UI
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            //在主线程操作UI更新
        }];
    }];
    
    [queue3 addOperation:down];

以上差不多就是iOS中多线程的知识点了 ,如果哪里有错误,还希望告知一下

上一篇 下一篇

猜你喜欢

热点阅读