iOS中多线程知识总结(二)

时间:2023-03-08 18:12:23

1.GCD

GCD全称是Grand Central Dispatch,译为"强大的中枢管理器"

  1)什么是任务?什么是队列?

    任务和队列是GCD的核心.

    任务: 执行什么操作

    队列: 用来存放任务

2)用GCD创建线程的两种方式. 

    01.使用并发队列创建

//01 获得并发队列
/*
第一个参数:C语言的字符串 对队列的名称(com.520it.www.DownloadQueue)
第二个参数:队列的类型
DISPATCH_QUEUE_SERIAL 串行队列
DISPATCH_QUEUE_CONCURRENT 并发队列
*/
dispatch_queue_t queue = dispatch_queue_create("www.baidu.com", DISPATCH_QUEUE_CONCURRENT); //02 封装任务并把任务添加到队列
dispatch_async(queue, ^{
NSLog(@"download1---%@",[NSThread currentThread]);
});

         02.使用全局队列创建

//02 使用获得全局并发队列,开启子线程

        /*
第一个参数:队列的优先级
第二个参数:
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
//02 封装任务并把任务添加到队列
dispatch_async(queue, ^{
NSLog(@"download1---%@",[NSThread currentThread]);
});

3)同步,异步,并发,串行

    同步函数: 在当前线程中执行任务,不具备开线程的能力

    异步函数: 在新的线程中执行任务,具备开线程的能力

    并发队列: 多个任务并发执行

串行队列: 一个任务执行完之后,再执行下一个

  4)是否会开启线程

01 异步函数+并发队列:开启多条线程,并发执行任务
        02 异步函数+串行队列:开启一条线程,串行执行任务
        03 同步函数+并发队列:不开线程,串行执行任务
        04 同步函数+串行队列:不开线程,串行执行任务
        05 异步函数+主队列:不开线程,在主线程中串行执行任务
        06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)

  5) 为什么使用同步函数+主队列(串行队列)会发生死锁?

    首先要明白主队列是在主线程中,而同步函数不具备开启子线程的能力.假设A正在主线程中执行,而在A执行的过程中又需要去执行B.由于同步函数+主队列并不会开启子线程,只能去主线程中执行,而主线程中A正在执行.由A需要C去执行,C又需要等A执行完毕,才能执行,就会造成死锁.

6) GCD中常用的函数

    1)一次性代码,整个程序运行过程中只执行一次,可以用作创建单例,线程安全

static dispatch_once_t onceToken;

    //内部的实现原理:最开始的时候onceToken==0 如果onceToken==0 那么就执行一次,onceToken=-1
NSLog(@"%zd",onceToken);
dispatch_once(&onceToken, ^{
NSLog(@"once");
});

    2)延迟函数

dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_SERIAL);
/*
延迟2秒,然后再把任务提交到队列
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{ NSLog(@"GCD----%@",[NSThread currentThread]);
});

    3)遍历函数

//快速迭代(并发队列):会开启子线程和主线程一起执行任务,所有的任务并发执行
/*
第一个参数:要遍历的次数
第二个参数:队列 ~ 线程 全局并发队列 == 自己创建的并发队列
自己创建的串行队列 == for循环
主队列:死锁
*/
dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(, queue, ^(size_t i) {
NSLog(@"%zd---%@",i,[NSThread currentThread]);
});

    4)栅栏函数

//需求:1)有四个任务,开子线程并发的执行这四个任务
//2)添加任务+++++++,但是要求必须要等1|2都执行完才执行++++,必须要等+++打印执行完才能执行后面的任务
//3)所有的任务都在子线程中执行(dispatch_barrier_async)
//栅栏 = 篱笆
//01 获得并发队列
dispatch_queue_t queue = dispatch_queue_create("TestQueue", DISPATCH_QUEUE_CONCURRENT);
//注意:!!!! 栅栏函数在使用中不能使用全局并发队列(会丧失拦截的功能)
//dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //02 异步函数
dispatch_async(queue, ^{
NSLog(@"1----%@",[NSThread currentThread]);
}); dispatch_async(queue, ^{
NSLog(@"2----%@",[NSThread currentThread]);
}); //特点:拦截上面的任务必须等前面的任务执行完才执行当前的block,必须等当前的block快执行完才执行后面
//dispatch_barrier_async 子线程中执行
//dispatch_barrier_sync 当前线程
dispatch_barrier_async(queue, ^{
NSLog(@"+++++%@",[NSThread currentThread]);
}); dispatch_async(queue, ^{
NSLog(@"3----%@",[NSThread currentThread]);
}); dispatch_async(queue, ^{
NSLog(@"4----%@",[NSThread currentThread]);
});

4:NSOperation(操作队列)

1)基本概念

    NSOperation本身是抽象类,只能使用它的子.分别是NSBlockOperation、NSInvocationOperation以及自定义继承自NSOperation的类

2)操作队列的核心是: 队列 + 操作

    其实它的使用也很简单,就是先创建队列,然后把你封装的操作加到队列,操作队列默认的是并发队列

  3)NSInvocationOperation的使用(使用不多)   

//01 创建队列
/*
操作队列:
① 自己创建(自定义)[并发队列*串行队列,默认是并发队列] [[NSOperationQueue alloc]init]
② 主队列[串行队列] [NSOperationQueue MainQueue]
*/
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //02 封装操作
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil]; NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download) object:nil]; //03 把操作添加到队列
[queue addOperation:op1];
[queue addOperation:op2];

    4)NSBlockOperation

      1)怎么创建

//01 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //02 封装操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1----%@",[NSThread currentThread]);
}]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2----%@",[NSThread currentThread]);
}]; //03 把操作添加到队列
[queue addOperation:op1]; //addOperation 内部调用start方法
[queue addOperation:op2];

      2) 设置依赖和监听(通常在下载的时候用的到)

//01 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSOperationQueue *queue2 = [[NSOperationQueue alloc]init]; //02 封装操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1----%@",[NSThread currentThread]);
}]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2----%@",[NSThread currentThread]);
}]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3----%@",[NSThread currentThread]);
}]; NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"4-下载电影-%@",[NSThread currentThread]);
}]; NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"+++++++5+%@+++",[NSThread currentThread]);
}]; //设置监听 |当op4任务结束的时候会执行block中的代码
//completionBlock 在子线程中执行
op4.completionBlock = ^{
NSLog(@"我已经被下载完了,快点来看我吧--%@",[NSThread currentThread]);
}; //设置依赖
//5-4-3-2-1
[op1 addDependency:op2]; //必须要等任务2执行完毕才能执行任务1
//[op2 addDependency:op1]; !!! 不能设置循环以来 [op2 addDependency:op3];
[op3 addDependency:op4];
[op4 addDependency:op5]; //03 把操作添加到队列
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue2 addOperation:op5];

输出结果:

iOS中多线程知识总结(二)

    5)设置最大并发数

/.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init]; //2.设置最大并发数
//注意点:该属性需要在任务添加到队列中之前进行设置
//该属性控制队列是串行执行还是并发执行
//如果最大并发数等于1,那么该队列是串行的,如果大于1那么是并行的
//系统的最大并发数有个默认的值,为-1,如果该属性设置为0,那么不会执行任何任务
queue.maxConcurrentOperationCount = ;

    6)暂停和恢复以及取消

//设置暂停和恢复
//suspended设置为YES表示暂停,suspended设置为NO表示恢复
//暂停表示不继续执行队列中的下一个任务,暂停操作是可以恢复的
if (self.queue.isSuspended) {
self.queue.suspended = NO;
}else
{
self.queue.suspended = YES;
} //取消队列里面的所有操作
//取消之后,当前正在执行的操作的下一个操作将不再执行,而且永远都不在执行,就像后面的所有任务都从队列里面移除了一样
//取消操作是不可以恢复的
[self.queue cancelAllOperations];

最后关于GCD 和 NSOperation的对比

 )GCD是纯C语言的API,而操作队列则是Object-C的对象。
)在GCD中,任务用块(block)来表示,而块是个轻量级的数据结构;相反操作队列中的『操作』NSOperation则是个更加重量级的Object-C对象。
)具体该使用GCD还是使用NSOperation需要看具体的情况 NSOperation和NSOperationQueue相对GCD的好处有:
)NSOperationQueue可以方便的调用cancel方法来取消某个操作,而GCD中的任务是无法被取消的(安排好任务之后就不管了)。
)NSOperation可以方便的指定操作间的依赖关系。
)NSOperation可以通过KVO提供对NSOperation对象的精细控制(如监听当前操作是否被取消或是否已经完成等)
)NSOperation可以方便的指定操作优先级。操作优先级表示此操作与队列中其它操作之间的优先关系,优先级高的操作先执行,优先级低的后执行。
)通过自定义NSOperation的子类可以实现操作重用,

附:

最近拜读了文顶顶大牛的博客,确实牛,强烈推荐......