一  多线程
二 NSThread
三 NSOperationQueue
四 GCD
五 多线程管理
 
 
一  多线程
1 术语 
程序:由源代码生成的可执行应用。(例如:QQ.app)
进程:一个正在运行的程序可以看做一个进程,进程拥有独立运行所需要的全部资源, 每个进程,都在自己独立的内存空间运行,并且进程之间互不影响
线程:程序中独立运行的代码段
进程和线程关系 : 一个进程是由一个或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。 进程的所有任务,都是在线程执行的,可以说它是进程的基本执行单元。
串行: 单个进程中执行的任务,是串行,每个任务都是按顺序执行的
单线程: 单个线程,同一时间内,只能执行一个任务
主线程 :每个正在运行的程序(既进程),至少包含一个线程,这个线程叫主线程 。主线程在程序启动时被创建,用于执行 main 函数
单线程程序 : 只有一个主线程的程序,称作单线程程序
单线程程序特点:在单线程程序中,主线程负责执行程序的所有代码(UI展现以及刷新,网络请求,本地存储等等),这些代码只能顺序执行,无法并发执行。
 
多线程程序:拥有多个线程的程序。
子线程:iOS允许用户自己开辟新的线程,相对于主线程来讲,这些线程,就是子线程。开发者可以根据需要开辟若干子线程。
子线程和主线程特点:都是独立的运行单元,各自的执行互不影响,因此能够并发执行。
 
3进程和线程的比较:
进程 :进程是 CPU 分配资源和调度的单位; 同一个进程中的线程,共享该进程下的资源
线程  线程是 CPU 调用(执行任务)的最小单位 一个进程可以有多个线程
 
4 单、多线程区别:
单线程程序:只有一个线程,既主线程,代码顺序执行,容易出现代码阻塞(页面假死)。
多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能
 
4.1 多线程
a 概念:一个进程可以有多个线程,每个线程可以并行(同时)执行不同的任务(下载一个软件,可以使用三个线程,同时下载不同的文件)
 
原理
b.1 统一时间,CPU只能调用一条线程,也就是说同一时间只有一条线程在工作
b.2并行,就是CPU(特指单核)以最快的速度,在不同线程之间进行切换、调度,从而造成了并行的假🐘
b.3 多核CPU,每个核心都可以同时处理不同任务,从而真正达到了多线程并发执行任务
 
c 多线程优缺点:
c.1优点
c.1.1提高程序运行效率
c.1.2提高了资源利用率,充分使用了空间的内存和CPU资源
c.2 缺点
c.2.1 每创建一条线程,都会开辟新的内存空间,并且消耗大约90毫秒的时间
c.2 2 如果开辟了过多的线程,势必会拖慢了 CPU 的运行速度,降低程序性能,让CPU开销过大
c.2.3 多线程设计上往往有些困难,线程间的切换、主线程子线程的协调,都是开发中较难解决的问题
 
4.2多线程在iOS 开发中的应用
1 主线程
1.1 应用程序启动之后,在UIApplication 中会自动开启一条主线程(UI线程)
1.2 主线程的作用,主要是用来显示/刷新UI界面,处理UI事件
2 使用时的注意事项
2.1 耗时的操作(加载事件过长的操作),不要放在主线程里,否则会影响主线程处理UI事件,导致运行界面拖慢,降低用户体验
2.2 当耗时操作还没有结束时,UI界面无法响应用户的交互,直到耗时操作结束后,主线程才能继续处理UI事件
2.3 因此:耗时操作,应该放在子线程中去执行(例如后台线程)
注意:iOS 中关于UI的添加和刷新必须在主线程中操作
 
二 iOS 平台下的多线程---------- iOS 多线程实现种类
1 NSThread 
2 NSOperationQueue
3 NSObject
4 GCD
 
三 NSThread
1    NSThread
优点 轻量级的多线程
缺点 需要自己管理线程的生命周期,线程同步
2  创建方法
2.1   NSThread的几个属性
currentThread   获取当前线程
mainThread      获取主线程
isMainThread    判断是否为主线程,返回BOOL
sleepForTimeInterval  线程休眠时间
 
2.2 创建子线程的方法
:NSObject开辟子线程(在后台执行某个方法)
performSelectorInBackground:@selector(SayHi) withObject:nil
参数
performSelectorInBackground  需要使用子线程执行的方法
     withObject         传递的参数
b:NSThread手动开辟子线程 
//b1 创建线程(NSThread
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(SayHi) object:nil];
//b2 开启线程
    [thread start];
//b3 关闭线程(可写可不写)
    [NSThread exit];
//b4 取消线程(实际上就是做了标记,表示被取消了)
    [thread cancel];
C:NSThread自动开辟子线程
//延迟执行的方法
   [NSThread sleepForTimeInterval:5];
//开辟
   [NSThread detachNewThreadSelector:@selector(SayHi) toTarget:self withObject:nil];
参数 同 上
SayHi里面执行如下方法
[self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:NO];
参数
//使用 NSObject 回到主线程
    /*
     performSelectorOnMainThread:回到主线程之后需要写的方法
     withObject:    传递的参数
     waitUntilDone:  判断 方法 是否执行完毕 
     YES:先执行方法,在回到该函数,执行该函数后面的语句
     NO:先执行函数和函数后面的语句 ,后执行方法
     */
 
注意 : 
1 每个线程都维护着与自己对应的 NSAutoreleasePool 对象,将其放在线程栈的栈顶。当线程结束时候,会清空自动释放池
2 为保证对象的及时释放,在多线程方法中需要添加自动释放池
3 在应用程序打开的时候,系统会自动为主线程创建一个自动释放池
4 我们手动创建的子线程需要我们手动添加自动释放池
 
四 NSObject 实现异步后台执行
1  NSObject 开辟线程
:NSObject开辟子线程(在后台执行某个方法)
performSelectorInBackground:@selector(SayHi) withObject:nil
参数
performSelectorInBackground  需要使用子线程执行的方法
     withObject         传递的参数
 
五 NSOperation 和 NSOperationQueue
1 NSOperation 类
1.1 首先,在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类,
1.2他是一个抽象类,不能直接使用这个类。所以执行任务的是它的子类:
    NSInvocationOperationNSBlockOperation .这两个子类,相当于一个方法选择器“prefromSelector()”,由它两本身发起的任务,并不是在子线程中执行
1.3 NSOperation 和它的子类,本身并不会进行线程的创建,所以,在他们的任务方法中打印当前线程,显示为主线程
1.4 NSOperation 和它的子类,只是一个操作,本身没有主线程、子线程之分,可以在任何线程中使用,通常和NSOperationQueue 结合使用
1.5 代码 NSOperation本身和多线程没有任何关系,它只是封装了一定的代码段和数据去实现一个功能
1.5.1  NSInvocationOperation封装了执行操作的target 和要执行的action
 NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(SayHe) object:nil]; //NSInvocationOperation 对象在单独使用的时候,需要手动调用开启方法
////    [operation start];
1.5.2 NSBlockOperation 封装了需要执行的代码块
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
//        NSLog(@"%@",[NSThread currentThread]);
//    }]; //    //开启
////    [blockOperation start];
注意:NSOperationQueue 创建多线程,如果搭配了 NSOperationQueue 中的 add 方法创建多线程的话,就不需要使用 Start 方法,否则会崩溃
 
2 NSOperationQueue
2.1一个 NSOperationQueue 操作队列,就相当于一个线程管理器,管理一组Operation对象的执行,将 NSOperation 和子类的对象放入队列中,然后由队列负责派发任务,所以 NSOperationQueue 并不是一个线程。但是,你可以设置队列中运行的线程的数量
2.2 其中 NSOperation可以调节它在队列中的优先级(使用 addDependency:设置依赖关系)
2.3当最大并发数设置为 1 的时候,能实现线程同步(串行执行)
----优点-----
1 不需要手动关联线程,只需要把精力放在自己要执行的操作上面
----缺点-----
1 它是基于 OC对象,那么相对于 C函数来说,效率要低,而且基于 GCD,那么GCD 提供的功能比它更全面
 
2.4 代码实现 NSOperationQueue 操作队列,就相当于一个线程管理器, 所以它可以根据Operation 任务自己分配任务,自己管理线程的声明周期,而且,用NSOperationQueue 创建的,是 N个并行队列
a  //初始化
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
b  //设置最大并发数
    //当设置最大并发数为1 时,也可叫串行,顺序执行
    //当设置最大并发数大于1时,叫并行,多条通道同时进行,互不影响
    queue.maxConcurrentOperationCount = 1;
C //测试最大并发数
    for (int i = 0; i<10; i++) {
        NSBlockOperation *block = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"%@",[NSThread currentThread]);
        }];
        [queue addOperation:block];
    }
 
六 GCD (Grand Central Dispatch)
     是Apple开发的一种多核编程技术,主要用于优化应用程序以支持多核处理器以及其他对称多处理系统,GCD提供函数实现多线程开发,性能更强大。
1、特点:
 1.1 C语言编写,所以在使用的时候,使用的是函数而不是方法。
 1.2 GCD可以充分利用多核硬件并发处理多个任务,也就是说,效率高。
 1.3 GCD使用后,不用程序去管理线程的开闭,GCD会在系统城上面动态的检测系统状态,开闭线程。
 1.4 管理线程的生命周期(调度任务、销毁线程、创建线程)
2、核心概念:
 2.1 任务:执行什么操作(具有一定功能的代码段,一般是一个block或者函数)
 2.2 分发队列:用来存放任务(GCD以队列的方式进行工作,FIFO)
   2.3 GCD 会根据分发队列的类型,创建合适数量的线程执行队列中的任务  

3、使用GCD的两个步骤
 3.1 定制任务:确定需要做什么事情
 3.2 将任务添加到队列中:
  3.2.1 GCD会自动将队列中的任务取出,放到对应的线程中。
  3.2.2 而任务的取出,遵循队列的先进先出(FIFO)原则。
4、队列
 4.1 并发队列
  4.1.1  可以让多个任务并发(同时)执行(自动开启了多个线程,同时执行任务)
  4.1.2 并发功能只能在异步函数下才会有效
 4.2 串行队列:让任务一个接一个执行(一个完成,执行下一个)
 4.3 并发和串行,决定了任务的执行方式
  4.3.1 并发:多个任务 同时 执行
  4.3.2 串行:多个任务 挨个 执行
4.3 dispatch queue 分两种:
SerialQueue :一次执行一个任务。 Serial Queue 通常用于特定的资源或数据。当你创建多个Serial queue 时,虽然它们各自是同步执行的,但 Serial queue 与 Serial queue之间是并发执行的。SerialQueue 能实现线程同步
Concurrent :可以并发的执行多个任务,但是遵守 FIFO
 
5 代码
5.1 获取串行队列代码
第一种:系统提供的串行队列创建方式(串行队列中比较实用的,常用在开发中
dispatch_queue_t queue1 = dispatch_get_main_queue();//主队列
里面的任务在主线程依次去执行
同步: 同步执行任务:在一个线程当中,让事情有序的执行(只能在当前线程中执行,不能开辟新线程)
dispatch_sync(queue2, ^{
        NSLog(@" %@",[NSThread currentThread]);
    });
异步: 异步执行任务:在另一个线程中执行(可以在新线程中开启任务,具有开辟新线程的能力)
dispatch_async(queue2, ^{
        NSLog(@" %@",[NSThread currentThread]);
    });
注:一般都会使用异步开辟新的线程去执行任务
//第二种:自己创建的队列,第一个参数是队列的名字(苹果推荐使用反向域名去命名),第二个参数队列的类型(串行队列、并行队列),这种方式创建的队列,它会自己去开辟一个子线程去完成队里面的任务。
    dispatch_queue_t queue2 = dispatch_queue_create(“com.lanou3g.mySeria", DISPATCH_QUEUE_SERIAL);
注:队列类型可以写0 ,系统默认为 串行
 
5.1 获取并列队列代码
 //第一种:系统方法创建并发队列(是苹果了里面的全局队列,有四个优先级)
第一个参数就是队列的优先级,第二个参数是苹果预留的参数为了以后去使用,目前没有用到,填写0
 
    dispatch_queue_t queueS = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
   
//第二种:自己创建并发队列  参数同上
    dispatch_queue_t queueM = dispatch_queue_create("queueM", DISPATCH_QUEUE_CONCURRENT);
 
 
5.3 延迟执行一段代码
  dispatch_after(
     参数一:dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC))表示:【计算时间(从现在开始计时,(int64_t)(真正延迟的时间 * NSEC_PER_SEC))
     参数二:dispatch_get_main_queue(), ^{
     任务;
     });   
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"已经3秒了");
    });
5.4  GCD功能
5.4.1 dispatch_async() // 往队列中添加任务,任务会排队执行
参数同上(异步)
5.4.2dispatch_after()//往队列中添加任务,任务不但不会排队,还会在延迟的时间点执行
参数同上
5.4.3dispatch_apply()//往队列中添加任务,任务会重复执行 n 次
     dispatch_apply
     (
     参数一:size_t iterations:执行的次数
     参数二:dispatch_queue_t queue:队列(在哪个队列里面去执行)
     参数三:^(size_t 写一个索引) {
     参数4 :任务
     });
     */
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"index__%zu",index);
    });
5.4.4 dispatch_group_async()//将任务添加到队列中,并添加分组标记
a  dispatch_group_t group = dispatch_group_create(); 
    作用:主要用于把一些不相关的任务归为一组,组里面放的是队列
b   dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
 
c    dispatch_group_async(group, queue, ^{
     作用:向分组中的队列添加任务
        NSLog(@"我是第一个任务");
    });
d dispatch_group_notify(group, queue, ^{
        NSLog(@"无论如何,我都是最后一个");
    });
注意:往分组里面的队列添加任务,最少要添加一个任务。否则,notify里面的任务不会等待小组里面的其他任务执行完才执行
5.4.5 dispatch_group_notify()//将任务添加到队列中,当某个分组的所有任务完成之后,此任务才会执行
作用: 监听组里面的任务,等到组里面的任务全部执行完成之后,才会执行它里面的任务
5.4.6 dispatch_barrier_async()//将任务添加到队列中,此任务执行的时候,其他任务停止执行
作用:在它之前和之后的任务都可以并发的去执行
5.4.7 dispatch_once()//任务添加到队列中,但任务在程序运行过程中,只执行一次
作用:该函数接收一个dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整形,实际上作为BOOL使用)。它还接收一个希望在应用的生命周期内仅仅被调度一次的代码块。
dispatch_once不仅意味着代码仅仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题
自定义単例
5.4.8 dispatch_sync()//将任务添加到队列中,block不执行完,下面的代码不会执行
5.4.9  dispatch_async_f()//将任务添加到队列中,任务是函数非block
 dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work)
第一个参数:队列
第二个参数:函数参数的内容
第三个参数:函数
5.4.10 async 和sync 的区别
async 不等block 体执行完,就去执行下面的代码
sync 会等待 block 体执行完成之后,才会去执行block体外面的代码
 
七 线程间通信
1 主线程进入子线程(前面的方法都可以)
2 子线程回到主线程
GCD :dispatch_queue_t queue1 = dispatch_get_main_queue()
NSObject: performSelectorOnMainThread: withObject:waitUntilDone: modes:
3 回到指定线程
 
八 线程互斥