iOS进阶学习-多线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS进阶学习-多线程相关的知识,希望对你有一定的参考价值。

一、多线程概述

  1、程序,进程,线程

  • 程序:由源代码生成的可执行应用。(例如:QQ.app)
  • 进程:一个正在运行的程序可以看做一个进程。(例如:正在运行的QQ就是一个进程),进程拥有独立运行所需的全部资源。
  • 线程:程序中独立运行的代码段。(例如:接收QQ消息的代码)
  • 一个进程是由一或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。

  2、单线程

  • 每个正在运行的程序(即进程),至少包含一个线程,这个线程叫主线程。
  • 主线程在程序启动时被创建,用于执行main函数。
  • 只有一个主线程的程序,称作单线程程序。
  • 在单线程程序中,主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等)。这些代码只能顺序执行,无法并发执行。

  3、多线程

  • 拥有多个线程的程序,称作多线程程序。
  • ios允许用户自己开辟新的线程,相对于主线程来讲,这些线程,称作子线程。
  • 可以根据需要开辟若干子线程
  • 子线程和主线程 都是 独立的运行单元,各自的执行互不影响,因此能够并发执行。

  4、单,多线程的区别

  • 单线程程序:只有一个线程,即主线程,代码顺序执行,容易出现代码阻塞(页面假死)。
  • 多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能。
  • 注意:iOS中关于UI的添加和刷新必须在主线程中操作。

二、NSThread

  实现多线程方式之一:NSThread,它是一个轻量级的多线程。它有以下两种创建方法:

#pragma mark - NSThread手动开辟子线程
     // 参数1:target
    // 参数2:方法
    // 参数3:传参
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sayHi) object:nil];
    [thread start];
    // 使用NSThread和NSObject实现的开辟线程,系统会自动释放,所以不需要自己手动关闭。
    // 结束线程的两种方式
    // 取消线程(给线程发送结束消息,通过这个消息进行取消)
    [thread cancel];
    // 立即结束线程
    [NSThread exit];
#pragma mark - NSThread自动开辟子线程  
    // 线程自动开始
    // 把手动开启的target和selector顺序颠倒
    [NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];
    
    // 获取当前线程
    NSLog(@"current == %@", [NSThread currentThread]);
    // 获取主线程
    NSLog(@"mainThread == %@", [NSThread mainThread]);
    // 判断当前线程是否为主线程
    NSLog(@"isMainThread == %d", [NSThread isMainThread]);

  NSObject中存在了一个最简单的后台执行的方法:

#pragma mark - NSObject开启子线程
    /**
     *  开启子线程的方式之一:NSObject
     */
    // 使用performSelectorInBackground开辟子线程
    // 第一个参数:selector
    // 第二个参数:方法传递的参数
    [self performSelectorInBackground:@selector(sayHi) withObject:@"test"];
  - (void)sayHi
{
    // 回到主线程
    // 参数1:selector
    // 参数2:传参
    // 参数3:是否等待子线程完成之后进入主线程
    [self performSelectorOnMainThread:@selector(mainThreadChangeColor) withObject:nil waitUntilDone:YES];  
    
}

三、NSOperationQueue

  1、NSOperation

  • NSOperation类,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类。
  • 因为它是抽象的,不能够直接使用这个类,而是使用子类( NSInvocationOperation或NSBlockOperation )来执行实际任务。
  • NSOperation(含子类),只是一个操作,本身无主线程、子线程之分,可在任意线程中使用。通常与NSOperationQueue结合使用。
  • 注意:在使用NSOperation的子类去创建线程的时候,实际线程没有真正意义上的创建。

  2、NSInvocationOperation

  • NSInvocationOperation是NSOperation的子类
  • 封装了执行操作的target和要执行的action。
    /**
     *  NSOperation不能直接进行多线程的创建,需要借助:NSOperationQueue
     */
    // 使用NSOperation的第一个子类去创建子线程:NSInvocationOperation
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
    // 在单独使用NSOperation的子类去创建线程的时候,一定要启动才行
    [operation start];

  3、NSBlockOperation

  • NSBlockOperation是NSOperation的子类
  • 封装了需要执行的代码块
    //使用NSOperation的第二个子类创建子线程
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"我是block");
        NSLog(@"-------%@", [NSThread currentThread]);
        NSLog(@"********%@", [NSThread mainThread]);
    }];
    [blockOperation start]; 
  •  需要把上边的两个线程,放到操作队列里,才是真正意义上的创建子进程
  • addOperation一旦将创建的对象加入到操作队列中,就不能调用start方法,否则程序会crash
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:operation];
    [queue addOperation:blockOperation];

  4、NSOperationQueue

  • NSOperationQueue是操作队列,他用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行。
  • 其中NSOperation可以调节它在队列中的优先级(使用addDependency:设置依赖关系)。
  • 当最大并发数设置为1的时候,能实现线程同步(串行执行)。
    // 创建队列的对象
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 当值设置为1的时候,可以叫做串行:即顺序执行
    // 当设置大于1的时候,叫做并行:多条通道同时进行各自的任务
    queue.maxConcurrentOperationCount = 2;
    
    for (int i= 0; i < 10; i++) {
        NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"current = %@, da = %@, i = %d", [NSThread currentThread], [NSThread mainThread], i);
        }];
        [queue addOperation:blockOperation];
    }

四、GCD

  1、GCD简介

  • Grand Central Dispatch (GCD)是Apple开发的一种多核编程技术。主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。
  • GCD提供函数实现多线程开发,性能更高,功能也更加强大。
  • 它首次发布在Mac OS X 10.6 ,iOS 4及以上也可用。

  2、GCD核心概念

  • 任务:具有一定功能的代码段。一般是一个block或者函数。
  • 分发队列:GCD以队列的方式进行工作,FIFO。
  • GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务。

  3、GCD中两种队列(dispatch queue

  • SerialQueue:一次只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。SerialQueue能实现线程同步。
#pragma mark - 使用GCD去创建一个串行队列
    // 第一种:系统提供的创建串行队列的方法
     dispatch_queue_t queue = dispatch_get_main_queue();//在真正的开发中如果需要创建串行队列,比较习惯用这种
    // 第二种:自己创建
    // 参数1:系统提供的一个宏
    // 参数2:是系统的保留字段
    // 参数1和2可以互换位置,位置没有严格要求
    dispatch_queue_t queue = dispatch_queue_create(DISPATCH_QUEUE_SERIAL, 0); 
  • Concurrent:可以并发地执行多个任务,但是遵守FIFO
#pragma mark - 使用GCD去创建一个并行队列
    // 第一种:系统提供的
    // 参数1:优先级(有四个,没有明显的区别DISPATCH_QUEUE_PRIORITY_DEFAUL,    DISPATCH_QUEUE_PRIORITY_HIGH,    DISPATCH_QUEUE_PRIORITY_LOW,  DISPATCH_QUEUE_PRIORITY_BACKGROUND
// 参数2:系统的保留字段
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    // 第二种:自己创建
    // 参数1:表示创建队列的名字(苹果推荐使用反向域名去命名)
    // 参数2:系统提供的一个宏(队列的类型)
    dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);

  4、GCD功能

  • dispatch_async()    //往队列中添加任务,任务会排队执行
  • dispatch_after()      //往队列中添加任务,任务不但会排队,还会在延迟的时间点执行
  • dispatch_apply()    //往队列中添加任务,任务会重复执行n次
dispatch_async(queue, ^{
        NSLog(@"currentThread = %@", [NSThread currentThread]);
        NSLog(@"mainThread = %@", [NSThread mainThread]);
    });

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"3.0秒之后输出");
    });

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%ld", index);
    });
  • dispatch_group_async()   //将任务添加到队列中,并添加分组标记
  • dispatch_group_notify()    //将任务添加到队列中,当某个分组的所有任务执行完之后,此任务才会执行
  • dispatch_barrier_async()  //将任务添加到队列中,此任务执行的时候,其他任务停止执行
    // 创建一个分组
    dispatch_group_t group = dispatch_group_create();
    // 创建一个并行队列
    dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        NSLog(@"我是任务1");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"我是任务2");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"我是任务3");
    });
    
    // 用于监听所有任务的执行情况的(所以此功能代码必须放在所有任务之后书写)
    dispatch_group_notify(group, queue, ^{
        NSLog(@"我是在所有任务之后执行的");
    });
   dispatch_barrier_async(queue, ^{
        NSLog(@"我执行了");
    });
  • dispatch_once(),不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题。
  • 通过GCD创建单例
+ (MyHandle *)sharedMyHandle
{
    // 在GCD中只执行一次,用于记录内容是否执行过
    static dispatch_once_t onceToken;
    // 保证多线程并发执行时只执行一次
    dispatch_once(&onceToken, ^{
        handle = [[MyHandle alloc] init];
    });
    return handle;
}
  • dispatch_sync()   //将任务添加到队列中,block不执行完,下面代码不会执行
  • async和sync的区别:
// async 不等 block 体执行完。。就去执行下面的代码
// sync会等待 block 体执行完成之后,才会去执行 block 体外面的代码
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_sync(queue, ^{
        NSLog(@"第一个任务");
    });
    NSLog(@"呵呵");
    dispatch_sync(queue, ^{
        NSLog(@"第二个任务");
    });
    NSLog(@"哈哈");
  • dispatch_async_f()  //将任务添加到队列中,任务是函数非block
// 函数
void function(void * str){
    NSLog(@"这是一个函数,%s",str);
}

// 第一个参数:队列
// 第二个参数:函数参数的内容
// 第三个参数:函数
dispatch_queue_t queue  = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async_f(queue, @"passValue", function);

 

以上是关于iOS进阶学习-多线程的主要内容,如果未能解决你的问题,请参考以下文章

iOS进阶之多线程

Python进阶第二篇多线程消息队列queue

python进阶学习--多线程编程

java进阶学习--java多线程

Python学习笔记——进阶篇第八周———Socket编程进阶&多线程多进程

Python学习笔记——进阶篇第八周———CPU运行原理与多线程