ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

GCD与NSOperation

2020-12-20 23:02:28  阅读:180  来源: 互联网

标签:GCD 队列 dispatch queue 任务 NSOperation 线程


GCD

 


 

> 什么是GCD

a、全称是Grand Center Dispatch,即牛逼的中枢调度器;

b、纯C语言,提供了非常多强大的函数;

> GCD的优势

a、GCD是苹果公司为多核的并行运算提出的解决方案;

b、GCD会自动利用更多的CPU内核(比如双核、四核);

c、GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程);

d、程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码;

> GCD的2个核心概念  

a、任务:执行什么操作(执行下载、播放音乐等);

b、队列:用来存放任务;

> GCD的使用步骤

a、定制任务[确定想要做的事情];

b、将任务添加到队列中[GCD会自动将队列中的任务取出,放到对应线程中执行;另外任务的取出是遵循队列的FIFO原则:先进先出];

> GCD基本使用

a、GCD中的2个用来执行任务的函数

// 同步方式执行任务[queue队列,block任务]

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

// 异步方式执行任务[queue队列,block任务]

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

b、GCD同步和异步的区别

- 同步:只能在当前线程中执行任务,不具备开启新线程的能力;

- 异步:可以在新的线程中执行任务,具备可启新线程的能力;

[具备开启线程的能力,但不代表一定会开启线程!!!]

c、GCD队列的两大类型

- 并发队列

多个任务并发(同时)执行(自动开启多个线程同时执行任务);

并发功能只有在异步(dispatch_async)函数下才有效;

- 串行队列

多个任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务);

d、容易混淆的术语[同步、异步、并发、串行]

- 同步和异步主要影响:具不具备开启新的线程 (并不代表一定会开线程!);

- 并行和串行主要影响:任务的执行方式;

e、并发队列

- GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建

dispatch_get_global_queue函数获得全局的并发队列

dispatch_queue_t dispatch_get_global_queue(long identifier, // 队列的优先级

unsigned long flags); // 参数保留,用0即可

  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

- 全局并发队列的优先级

     DISPATCH_QUEUE_PRIORITY_HIGH       高

     DISPATCH_QUEUE_PRIORITY_DEFAULT    默认(中)   

     DISPATCH_QUEUE_PRIORITY_LOW        低

     DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台

f、串行队列

- 使用dispatch_queue_create函数创建串行队列

dispatch_queue_t dispatch_queue_create(const char *label, // 队列名称

  dispatch_queue_attr_t attr); // 队列属性 dispatch_queue_t queue = dispatch_queue_create("syncSerialQueue", NULL); // 创建串行队列 

- 使用dispatch_get_main_queue()获得主队列(跟主线程相关的队列,用于线程间通信)

[主队列是GCD自带的一种特殊串行队列;放在主队列中的任务,都会放到主线程中执行]

g、延时执行

    /** 方式1,这种延时操作是不可取的,因为延时操作是在主线程,即会卡住主线程,如果sleep在其他线程则也会卡住对应线程*/

    // 延时3秒

    [NSThread sleepForTimeInterval:3];

 

// 方式2,定制好任务后,不会卡住当前线程

    [self performSelector:@selector(downloadImage) withObject:nil afterDelay:3];

 

     // 方式3

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        NSLog(@"图片下载");

    });

 

> GCD中线程间的通信

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{

        NSString *urlStr = @"http://pica.nipic.com/2007-11-09/200711912453162_2.jpg";

        NSURL *url = [NSURL URLWithString:urlStr];

        

        // 获取全局队列

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        // 添加任务 异步操作

        dispatch_async(queue, ^{

            NSLog(@"下载图片:%@",[NSThread currentThread]);

            

            // 下载图片

            NSData *data = [NSData dataWithContentsOfURL:url];

            UIImage *image = [UIImage imageWithData:data];

            

            // 返回到主线程显示

            dispatch_async(dispatch_get_main_queue(), ^{

                _imageView.image = image;

                NSLog(@"设置图片显示:%@",[NSThread currentThread]);

            });

        });

    }

 

> GCD一次性代码

    // 只执行一次

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        NSLog(@"---下载图片---");

    });

 


 

 NSOperation(基于GCD封装) 

> NSOperation和NSOperationQueue实现多线程的具体步骤:

a、将需要执行的操作封装到一个NSOperation对象中;

b、然后将NSOperation对象添加到NSOperationQueue中;

c、系统会自动将NSOperationQueue中的NSOperation取出来;

d、将取出来的NSOperation封装的操作放到一条新线程中执行;

【系统会自动处理,不用管多少条线程】

 

> NSOperation的基本使用

a、NSOperation是抽象类,不具备封装操作的能力,必须使用它的子类;

b、使用NSOperation子类的方式有3种

- NSInvocationOperation

- NSBlockOperation

- 自定义子类继承NSOperation,实现内部相应的方法

 

> NSInvocationOperation

// 创建操作

    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadImage) object:nil];

 

> NSBlockOperation

    /** 没有添加到队列中,直接调用operation的start方法

     当任务个数为一的时候,就是同步执行;

     当任务个数大于一的时候,就会异步执行;

     */

 

    // 手动开启 (没有添加到队列中,就需要手动开启)

    [operation start];

 

    // 添加到队列中 [自动异步执行]

    [queue addOperation:operation];

 

> NSOperationQueue

  // 1、创建一个队列(非主队列)

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];

        // 2、任务任务

        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{

            NSLog(@"operation1      下载图片---%@",[NSThread currentThread]);

        }];

    [operation1 addExecutionBlock:^{

        NSLog(@"operation2      下载图片---%@",[NSThread currentThread]);

    }];

 

// 3、添加到队列中(多个任务会自动异步执行任务,并发)

[queue addOperation:operation1];

 

 

// 更为简单的写法  自动异步执行任务,并发

        NSOperationQueue *queue = [[NSOperationQueue alloc] init];

        [queue addOperationWithBlock:^{

            NSLog(@"operation3      下载图片---%@",[NSThread currentThread]);

        }];

 

 

// 设置最大并发数[这可以更好的保证程序的性能],即控制并发执行最大线程数量,以节省内存空间

queue.maxConcurrentOperationCount = 2;

 

 

> 操作之间的依赖(面试题)

a、NSOperation之间可以设置依赖来保证执行顺序(注意不能相互依赖);

    // 添加依赖 (和添加到队列的先后顺序无关)

    [operationC addDependency:operationB];

    [operationB addDependency:operationA];

b、可以在不同queue中的NSOperation之间创建依赖;

 

> 线程间通信

        // 回到主线程设置显示

// [self performSelector:(SEL) onThread:(NSThread *) withObject:(id) waitUntilDone:(BOOL)];

// [self performSelectorOnMainThread:(SEL) withObject:(id) waitUntilDone:(BOOL)];

// dispatch_async(dispatch_get_main_queue(), ^{

//    _imageView.image = image;

// });

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            _imageView.image = image;

        }];

 

> 队列的取消、暂停、恢复

a、取消队列的所有操作

- (void)cancelAllOperations; 【在内存警告的时候可以添加上该方法】

[也可以调用NSOperation的- (void)cancel方法取消单个操作]

b、暂停和恢复队列

- (void)SetSuspended:(BOOL)b; 【YES表示暂停队列,NO表示恢复队列】

[滚动视图(注意表格视图也是继承自UIScrollView的)性能优化中可以使用到,开始拖动的时候暂停队列下载,拖动结束后恢复队列]

// 接收到内存警告

        - (void)didReceiveMemoryWarning{

            [super didReceiveMemoryWarning];

            // 取消队列操作

        }

 

        - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{

            // 开始拖动,暂停队列操作

        }

 

        - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

            // 结束拖动,恢复队列操作

        }

 


 

线程安全(在GCD以后,已经帮我们处理了这些问题)

    /** 线程安全问题

     实例:3个窗口卖票

        > 3个窗口同时卖票,即多条线程访问同一资源;

        > 线程哪个先调用是不确定的,先调度谁是CPU的事;

        > 当线程任务执行完成,线程为死亡态(如果是没有while循环,但第二次点击的时候程序就崩溃);

     */

> 多线程的安全隐患

a、资源共享(资源抢占):一块资源可能会被多个线程共享(即多个线程可能访问一个资源);

(当多个线程访问同一块资源时,很容易引发数据错乱和数据安全的问题)[例如一个线程取钱,一个线程存钱;例如卖票,多个窗口卖票即多条线程卖]

> 互斥锁的使用

a、互斥锁方式一:@synchronized(锁对象){//需要加锁的代码}

// 锁定一份代码只用一把锁,用多把锁是无效的;

b、互斥锁方式二:

/** 1、互斥锁的初始化*/

    _myLock = [[NSLock alloc] init];

// 2、加锁方式2

        [_myLock lock];

// 需要加锁的代码

        // 3、解锁

        [_myLock unlock];

> 互斥锁的优缺点

优点:能有效防止因多线程抢夺资源造成的数据安全问题;

缺点:需要消耗大量CPU资源;

> 互斥锁使用的前提:多条线程抢占同一资源;

> 单例的线程安全

> 数据库操作的线程安全

/**

 nonatomic(atomic):指定set、get方法是否原子操作。原子操作,主要指是否线程安全。如果使用atomic那么set、get方法都是线程安全的--- 当一个线程进入get、set方法之后,其他线程无法进入该set、get方法,那么久避免多线程并发破坏数据完整性,atomic是默认值。虽然atomic可以保证对象数据的完整性,但atomic线程安全会造成性能下降(因为会导致其他线程的阻塞)。因此,大多数单线程环境下,都是使用nonatomic来提高set、get方法的访问性能;

 */

 

 

线程注意点:

1、不要同时开太多线程(1~3条线程即可,不要超过5条);

2、线程概念:

主线程:UI线程,显示、刷新UI界面,处理UI控件事件;

子线程:后台线程,异步线程;

3、不要把耗时操作放在主线程,要放在子线程中执行;

标签:GCD,队列,dispatch,queue,任务,NSOperation,线程
来源: https://www.cnblogs.com/vkSwift/p/14165822.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有