iOS学习41之多线程

1. 多线程概述

 1> 程序、进程和进程的概念

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

 2> 单线程

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

 3> 多线程

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

 4> 单线程和多线程区别

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

2. NSThread与NSObject创建多线程

 1> NSThread创建方法

  

  • NSThread手动开辟子线程
    // 参数1:target
    // 参数2:方法
    // 参数3:传递的参数
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sayHi) object:nil];

    // 开启线程
    [thread start];
  • NSThread自动开辟子线程
   // 线程自动开启
    [NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];

 2> NSThread其他常用属性和方法

  头文件中其他常用属性和方法的声明:

// 获取当前的线程
+ (NSThread *)currentThread;

// 主线程休眠ti秒
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

// 直接将线程退出
+ (void)exit;

// 判断是否为主线程
+ (BOOL)isMainThread;

// 获取主线程
+ (NSThread *)mainThread;

// 取消线程,不是真正的取消,只是给线程发送一个信号,通过这份信号进行取消
- (void)cancel;

// 手动开辟子线程时启动线程
- (void)start;

  代码:

    // 获取当前线程
    NSLog(@"currentThread == %@", [NSThread currentThread]);

    // 获取主线程
    NSLog(@"mainThread == %@", [NSThread mainThread]);

    // 判断当前线程是否为主线程
    NSLog(@"isMainThread == %d", [NSThread isMainThread]);

 3> NSObject开启子线程

- (void)viewDidLoad {
    [super viewDidLoad];

    // 使用performSelectorInBackground开辟子线程
    // 参数1:selector
    // 参数2:方法传递的参数
    [self performSelectorInBackground:@selector(sayHi) withObject:nil];
    self.view.backgroundColor = [UIColor yellowColor];
}

// 子线程执行方法
- (void)sayHi
{

    NSLog(@"Hello World");

    NSLog(@"currentThread == %@", [NSThread currentThread]);

    NSLog(@"mainThread == %@", [NSThread mainThread]);

    // 回到主线程修改当前背景颜色
    // 参数1:selector
    // 参数2:传的参数
    // 参数3:是否等待子线程执行完成之后进入主线程
    [self performSelectorOnMainThread:@selector(mainThreadChangeColor) withObject:nil waitUntilDone:YES];
}

// 主线程执行方法
- (void)mainThreadChangeColor
{
    self.view.backgroundColor = [UIColor cyanColor];

    NSLog(@"currentThread == %@", [NSThread currentThread]);

    NSLog(@"mainThread == %@", [NSThread mainThread]);
}

 4> 线程结束

  使用NSThread和NSObject实现的开辟线程,系统会自动释放,关不关都可以

  结束线程的两种方式:

    // 取消线程
    [thread cancel]; // 不是真正的取消,只是给线程发送一个信号,通过这份信号进行取消

    // 直接将线程退出
    [NSThread exit];

3. NSOperationQueue

 1> 概述

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

 2> NSInvocationOperation

  • NSInvocationOperation是NSOperation的子类
  • 封装了执行操作的target和要执行的action。
    NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];

    // 在单独使用NSOperation的子类去创建线程的时候,一定要启动才行

    [invocationOperation start];

- (void)test
{
    NSLog(@"??");

    NSLog(@"currentThread Invocation == %@", [NSThread currentThread]);
    NSLog(@"mainThread Invocation == %@", [NSThread mainThread]);
}

 3> NSBlockOperation

  • NSBlockOperation是NSOperation的子类
  • 封装了需要执行的代码块
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"Block");

        NSLog(@"currentThread Block == %@", [NSThread currentThread]);
        NSLog(@"mainThread Block == %@", [NSThread mainThread]);
    }];

    // 只有启动的时候才会调用Block的内容
    [blockOperation start];

   :在使用NSOperation的子类去创建线程的时候,实际上没有真正意义上创建成功

  

  上图中输出的地址都是同一个地址,说明子线程没有创建成功

 4> NSOperationQueue

  • NSOperationQueue是操作队列,他用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行
  • 其中NSOperation可以调节它在队列中的优先级(使用addDependency:设置依赖关系)
  • 最大并发数maxConcurrentOperationCount

  当值设置为1时,可以叫串行:即顺序执行

  当值设置大于1时,叫做并行:多条通道同时进行各自的任务

  • 将 2> 和 3> 中创建的子线程添加到NSOperationQueue的操作队列
    // 需要把上边的两个线程,放到操作队列中
    // addOperation一旦将创建的对象加入到队列中,就不能使用start方法,否则或造成奔溃
    NSOperationQueue *operationQueue = [NSOperationQueue new];

    [operationQueue addOperation:invocationOperation];
    [operationQueue addOperation:blockOperation];

  :addOperation一旦将创建的对象加入到队列中,就不能使用start方法,否则或造成奔溃

  崩溃信息为:reason: ‘*** -[NSOperationQueue addOperation:]: operation is finished and cannot be enqueued

  请大家注意!

4. GCD

 1> 概述

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

 2> 核心概念

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

 3> GCD中的队列

  • 串行队列(SerialQueue):一次只执行一个任务。Serial queue通常用于同步访问特定的资源或数据。当你创建多个Serial queue时,虽然它们各自是同步执行的,但Serial queue与Serial queue之间是并发执行的。SerialQueue能实现线程同步
  • 并行队列(Concurrent):可以并发地执行多个任务,虽然遵守FIFO(先进先出),但由于各个任务被分配到不同的线程执行,因此其完成时间有可能不同,即:后分配的任务有可能先执行完成;并发队列一定需要和异步执行的任务(使用dispatch_async())结合起来使用才有意义。
  • 主队列:主队列也是一个串行队列,主队列中的任务都在主线程中执行。

 4> 串行队列(SerialQueue)

  • 第一种:系统提供的创建串行队列的方法,实际上主队列的创建方法
// 第一种获取方式:里面的任务是在主线程依次去执行,dispatch_get_main_queue()获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 往队列里面添加任务
dispatch_async(queue, ^{
NSLog(@"这是第一个任务,当前线程是:%@, 是否主线程 :%d ", [NSThread currentThread],  [[NSThread currentThread] isMainThread]);
});
  • 第二种:自己创建的队列
    // 参数1:队列的名字(苹果推荐使用反向域名去命名)
    // 参数2:队列的类型(串行队列、并行队列),这种方式创建的队列,它会自己去开辟一个子线程去完成队列里面的任务
    dispatch_queue_t queue = dispatch_queue_create("com.zf.mySerialQueue", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{
NSLog(@"这是第一个任务,当前线程是:%@, 是否主线程 :%d ", [NSThread currentThread],  [[NSThread currentThread] isMainThread]);
 });

 5> 并行队列

  • 第一种:系统提供的创建并行队列的方法
    // 参数1:优先级PRIORITY(有四个,没有明显的区别)
    // 参数2:系统保留字,苹果预留的参数为了以后去使用,目前没有用到,填写0
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_async(oneQueue, ^{
 NSLog(@"这是第一个任务。。。线程是:%@, 是否主线程:%d", [NSThread currentThread], [[NSThread currentThread] isMainThread]);
 });
  • 第二种:自己创建的队列

  使用的方法同串行队列,只有创建函数参数2给定队列的类型的时候,将DISPATCH_QUEUE_SERIAL替换为DISPATCH_QUEUE_CONCURRENT

    // 参数1:队列的名字(苹果推荐使用反向域名去命名)
    // 参数2:队列的类型(串行队列、并行队列),这种方式创建的队列,它会自己去开辟一个子线程去完成队列里面的任务
    dispatch_queue_t queue = dispatch_queue_create("com.zf.mySerialQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queue, ^{
NSLog(@"这是第一个任务,当前线程是:%@, 是否主线程 :%d ", [NSThread currentThread],  [[NSThread currentThread] isMainThread]);
 });

 6> GCD功能函数

  • dispatch_async(dispatch_queue_t queue, ^(void)block)    // 异步任务,往队列中添加任务,任务会排队执行

  参数1(dispatch_queue_t queue):添加任务的队列

  参数2(^(void)block):Block,主要是在添加过程中进行一些操作,操作代码就写在Block中

  • dispatch_after()      //往队列中添加任务,任务不但会排队,还会在延迟的时间点执行
    /*
     函数
     dispatch_after(dispatch_time_t when, dispatch_queue_t queue, ^(void)block);

     // 系统封装的代码快(一般使用)
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(具体的浮点型数字 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       延迟执行的内容
     });

     */
    // 参数1:延迟的时间,使用dispatch_time()初始化
    // 参数2:队列
    // 参数3:Block
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"3.0秒后");
    });

  dispatch_time(dispatch_time_t when, int64_t delta)对延迟时间进行初始化

  参数1:为起始时间,系统宏定义,一般使用 DISPATCH_TIME_NOW

  参数2:为延迟的具体时间类型为 int64_t,变量的类型为:具体浮点型数字 * 时间单位(系统的宏定义,可以根据实际情况选用)

  • dispatch_apply()    //往队列中添加任务,任务会重复执行n次
    /*
     dispatch_apply(size_t iterations, dispatch_queue_t queue, ^(size_t) {
        code
     });
     */
    dispatch_queue_t queue = dispatch_queue_create("com.zf.myQueue", DISPATCH_QUEUE_CONCURRENT);
    // 参数1:添加任务的个数
    // 参数2:队列
    // 参数3:Block,这个Block没有变量名,需要自己添加
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zu", index);
    });

  :函数dispatch_apply()的参数3Block没有变量名,需要自己添加

  • dispatch_group_async()   //将任务添加到队列中,并添加分组标记
  • dispatch_group_notify()    //将任务添加到队列中,当某个分组的所有任务执行完之后,此任务才会执行
//    dispatch_group_t主要用于把一些不相关的任务归为一组
//    组里面放的是队列
//    dispatch_group_async作用是往组里面的队列添加任务
//    dispatch_group_notify作用:监听组里面的任务,等到组里面的任务全部执行完成之后,才会执行它里面的任务

    //1、创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    //2、创建组
    dispatch_group_t group = dispatch_group_create();
         // 3、往组里面的队列添加任务(注意:在执行notify之前最起码要向队列中放置一个任务才可以,否则,notify里面的任务不会等待小组里面的其他任务执行完才执行。)
    dispatch_group_async(group, queue, ^{
         NSLog(@"这是第一个任务。。。线程是:%@, 是否主线程:%d", [NSThread currentThread], [[NSThread currentThread] isMainThread]);
    });

   dispatch_group_notify(group, queue, ^{
        NSLog(@"我是最后一个任务,组里面的其他任务都执行完毕之后,我就会执行");
    });
  • dispatch_barrier_async()  //将任务添加到队列中,此任务执行的时候,其他任务停止执行
// 数据库的读取。。。可以并发执行,通过 GCD 里面的并行队列去实现
// 数据库的写入。。。。只能串行执行,通过 GCD 里面的串行队列去实现
// 但是真正的项目,肯定是既有数据的读取,也有数据库的写入。。。如何解决该问题:dispatch_barrier_async 在它之前的任务可以并发去执行,在他之后的任务也可以去并发执行
   dispatch_queue_t queue = dispatch_queue_create("concurrentTest",DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
NSLog(@"这是第一个读取数据的任务。。。线程是:%@, 是否主线程:%d", [NSThread currentThread], [[NSThread currentThread] isMainThread]);
    });

    dispatch_barrier_async(queue, ^{
        NSLog(@"正在在数据库里写东西,不要打扰我");
    });

    dispatch_async(queue, ^{
NSLog(@"这是第二个读取数据的任务。。。线程是:%@, 是否主线程:%d", [NSThread currentThread], [[NSThread currentThread] isMainThread]);
    });
  • dispatch_once()   //任务添加到队列中,但任务在程序运行过程中,只执行一次

  dispatch_once: 该函数接收一个dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块。

  dispatch_once 不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题。

  运用代码(单例的完整定义)

static MyHandle *handle = nil;

+ (MyHandle *)sharedMyHandle
{
    // 在GCD中只执行一次,用于记录内容是否执行过
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        handle = [MyHandle new];
    });

    return handle;
}
  • dispatch_sync()   //同步任务 将任务添加到队列中,block不执行完,下面代码不会执行

  与 dispatch_async() 的区别:

   dispatch_async() 不等 block 体执行完,就去执行下面的代码,会在另外的线程中执行

   dispatch_sync() 会等待 block 体执行完成之后,才会去执行 block 体外面的代码,会在当前的线程中执行,当前线程有可能是主线程,也有可能是子线程

  • 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);
时间: 2024-07-29 20:15:17

iOS学习41之多线程的相关文章

iOS学习 --多线程和GCD的理解

进程的概念:每一个进程都是一个应用程序,都有独立的内存空间,一般情况下,一个应用存在一个进程,但也有多个进程的情况(比如浏览器) 同一个进程中的线程共享内存中的内存和资源. 多线程的概念:每一个程序都有一个主线程,调用main函数启动 主线程的生命周期和应用程序是绑定的,程序退出时,主线程也就停止了 多线程技术表示,使用多线程可以提高CPU的使用率,防止主线称堵塞. 任何有可能堵塞主线称的任务不要放在主线称执行(访问网络) 注意:线程的使用不是无节制的,只有主线称有直接修改UI的能力 iOS三种

ios学习记录 day41 UI17 多线程

CPU(工厂) 进程(车间) 线程(工人) 一个进程代表一个应用程序 CPU总是运行一个进程,其它进程处于非运行状态.一个进程可以包含多个线程.线程与线程之间可以共享进程的内存区域. 打开一个应用程序,系统会给我们创建一个线程,称为主线程 管理主界面的UI与内部循环机制(与界面相关的东西必须放在主线程中!!!) 压力比较大且会造成线程阻塞(界面卡),因此我们通过创建子线程来对主线程进行分压. 什么时候用多线程 1.网络请求(同步的) 2.文件读写(少) 3.大数据计算(冒泡) 4.数据库sele

IOS学习笔记 -- 多线程

多线程1.多线程的原理 1>.同一时间,CPU只能处理1条线程,只有1条线程在工作(执行) 2>.多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换) 3>.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象如果线程非常非常多,会发生: 1>.CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源 2>.每条线程被调度执行的频次会降低(线程的执行效率降低) 2.多线程的优缺点 1>.多线程的优点 能适当提高程序的执行效率 能适当提高资源

iOS 学习资料

(适合初学者) 本文资料来源于GitHub 一.视频教程(英文) Developing iOS 7 Apps for iPhone and iPad斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS SDK 的使用, 属于 iOS 基础视频 iPad and iPhone Application Development该课程的讲师 Paul Hegarty 是斯坦福大学软件工程学教授, 视频内容讲解得深入, 权威, 深受好评 Advanced iPhone Dev

iOS 学习资料整理集合

视频教程(英文) Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS SDK 的使用, 属于 iOS 基础视频 iPad and iPhone Application Development 该课程的讲师 Paul Hegarty 是斯坦福大学软件工程学教授, 视频内容讲解得深入, 权威, 深受好评 Advanced iPhone Development - Fall 2010 i

iOS 学习资料整理(转)

视频教程(英文) 视频教程(中文) 书籍 博客 文章 相关网站 社区 工具/插件 GitHub Top 50 简介 邮件订阅 文档 指南 Awesome 系列 知乎上的讨论 Quora 上的讨论 贡献者 License 这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的目前所处的位置. 该文档会持续更新, 同时也欢迎更多具有丰富经验的 iOS 开发

【超齐全】iOS 学习资料整理

好赶货,收藏.原文iOS 学习资料整理 这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的目前所处的位置. 该文档会持续更新, 同时也欢迎更多具有丰富经验的 iOS 开发者将自己的常用的一些工具, 学习资料, 学习心得等分享上来, 我将定期筛选合并, 文档尚有一些不完善之处, 也请不吝指出, 感谢您对 iOS 所做的贡献, 让我们一起把国内的 iO

iOS学习——并发编程GCD

在iOS中使用的多线程技术有四种,Pthread.NSThread.GCD.NSOperation,但GCD与OP严格来说,应该叫并发编程技术.GCD虽然是用C语言书写,但是苹果对它做了很多封装,让它使用起来及其简单方便,因此在OC开发中应用很广.而OP则是在iOS4.0之后对GCD进行了一次封装,增加了许多用GCD实现比较麻烦的功能,如最大并发数,线程暂停,取消以及依赖添加. GCD的使用其实可以拆分成两步,一是队列,二是任务/指令:队列分为串行队列.并发队列.全局队列以及主队列,其中主队列只

iOS 学习资料整理

视频教程(英文) 视频 简介 Developing iOS 7 Apps for iPhone and iPad 斯坦福开放教程之一, 课程主要讲解了一些 iOS 开发工具和 API 以及 iOS SDK 的使用, 属于 iOS 基础视频 iPad and iPhone Application Development 该课程的讲师 Paul Hegarty 是斯坦福大学软件工程学教授, 视频内容讲解得深入, 权威, 深受好评 Advanced iPhone Development - Fall