iOS多线程知识点总结

一、应用场景

  1. 异步下载数据,这是多线程技术的一个比较常见的应用场景
  2. 还有一些比较耗时的操作或者功能(客户端与服务端的交互;从数据库中一次性读取大量数据等),需要在主线程之外,单独的开辟一个新的线程(子线程/工作线程)来执行。

二、iOS支持的多线程编程方法

  1. NSThread
  2. NSOperation & NSOperationQueue
  3. GCD

四、线程的创建

  1. 创建后台线程,自动的开启线程
//performSelectorInBackground内部会创建一个线程 专门 执行调用 -thread1Click:
[self performSelectorInBackground:@selector(thread1Click:) withObject:@"线程1"];

2.先创建,自己手动启动线程

NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Click:) object:@"线程2"];
//设置名字
thread2.name = @"thread2";
//启动线程
[thread2 start];
//这种方式创建线程 需要手动启动

3.线程一旦创建,会立即执行

[NSThread detachNewThreadSelector:@selector(thread3Click:) toTarget:self withObject:@"线程3"];

五、界面假死

在开发中常常需要将耗时操作交给子线程/工作线程,而主线程去刷新UI界面

- (void)viewDidLoad {
    [super viewDidLoad];
#if 0
    [self func1];
    [self func2];
    [self func3];
    /*上面的写法是把耗时操作都交给UI主线程来完成,这样主线程只有把上面三个函数执行完成 才会继续向下执行
    这样的界面会假死,影响用户体验。
    我们应该把耗时的操作交给子线程来完成,异步完成
    */
#else
    //创建三个子线程 来完成 func1 func2 func3
    [NSThread detachNewThreadSelector:@selector(func1) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(func2) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(func3) toTarget:self withObject:nil]; 
#endif
}
- (void)func1 {
    for (NSInteger i = 0; i < 20; i ++) {
        NSLog(@"func1:i->%ld",i);
        [NSThread sleepForTimeInterval:0.5];
    }
    NSLog(@"func1即将结束");
}
- (void)func2 {
    for (NSInteger i = 0; i < 20; i ++) {
        NSLog(@"func2:i->%ld",i);
        [NSThread sleepForTimeInterval:0.5];
    }
    NSLog(@"func2即将结束");
}
- (void)func3 {
    for (NSInteger i = 0; i < 20; i ++) {
        NSLog(@"func3:i->%ld",i);
        [NSThread sleepForTimeInterval:0.5];
    }
    NSLog(@"func3即将结束");
}

六、监听线程结束

可以建立观察者,监听一个线程是否结束

- (void)viewDidLoad {
    [super viewDidLoad];
//要先注册观察者
//注册观察者的时候内部会专门创建一个线程监听其他线程有没有结束,
//当线程结束时,一般都会发送一个结束通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadWillEnd:) name:NSThreadWillExitNotification object:nil];
//子线程 一旦创建启动 就会和主线程 同时异步执行
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(thread1Click:) object:@"线程1"];
thread1.name = @"thread1";
[thread1 start];
    
NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Click:) object:@"线程2"];
thread2.name = @"thread2";
[thread2 start];   
}
- (void)thread1Click:(id)obj {
    //打印当前线程和主线程
    NSLog(@"thread:%@ isMain:%d",[NSThread currentThread],[NSThread isMainThread]);
    NSLog(@"%s",__func__);
    for (NSInteger i = 0; i < 10; i ++) {
        NSLog(@"func1_i:%ld",i);
        [NSThread sleepForTimeInterval:1];
    }
    NSLog(@"thread1即将结束");
}
- (void)thread2Click:(id)obj {
    NSLog(@"thread:%@ isMain:%d",[NSThread currentThread],[NSThread isMainThread]);
    NSLog(@"%s",__func__);
    for (NSInteger i = 0; i < 10; i ++) {
        NSLog(@"func2_i:%ld",i);
        [NSThread sleepForTimeInterval:0.2];
    }
    NSLog(@"thread2即将结束");
}
//当监听到线程结束的时候调用这个函数
//上面的两个线程结束都会调用这个函数
- (void)threadWillEnd:(NSNotification *)nf {
    NSLog(@"thread:%@ isMain:%d_func:%s",[NSThread currentThread],[NSThread isMainThread],__func__);
    NSLog(@"obj:%@",nf.object);//谁发的通知   
}

- (void)dealloc {
    //最后要移除观察者
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSThreadWillExitNotification object:nil];
}

七、多个线程之间的通信

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建3个线程
    _thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(thread1Click:) object:@"线程1"];
    [_thread1 start];
    
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(thread2Click:) object:@"线程2"];
    [thread2 start];
    
    NSThread *thread3  = [[NSThread alloc] initWithTarget:self selector:@selector(thread3Click:) object:@"线程3"];
    [thread3 start];
}
- (void)thread1Click:(id) obj {
    NSLog(@"%s",__func__);
    while (1) {
        if ([[NSThread currentThread] isCancelled]) {
            //要判断 当前这个线程 是否 被取消过(是否发送过取消信号)
            //如果被取消过,那么我们可以让当前函数结束那么这个线程也就结束了
            
            //break;//结束循环
            NSLog(@"子线程1即将结束");
            //return;//返回 函数
            [NSThread exit];//线程退出
        }
        
        NSLog(@"thread1");
        [NSThread sleepForTimeInterval:0.5];
    }
    NSLog(@"子线程1即将结束");
}
- (void)thread2Click:(id) obj {
    NSLog(@"%s",__func__);
    for (NSInteger i = 0; i < 10; i++) {
        [NSThread sleepForTimeInterval:0.2];
        NSLog(@"thread2:_i:%ld",i);
    }
    NSLog(@"子线程2即将结束");
    //当 thread2 即将结束之后 通知 thread1结束
    
    [_thread1 cancel];
    // cancel 在这里 只是给_thread1 发送了一个cancel 信号,最终thread1的取消,取决于 thread1的,如果要取消thread1,那么需要在thread1执行调用的函数内部 进行判断
}
- (void)thread3Click:(id) obj {
    NSLog(@"%s",__func__);
    for (NSInteger i = 0; i < 10; i++) {
        [NSThread sleepForTimeInterval:0.2];
        NSLog(@"thread3:_i:%ld",i);
    }
    NSLog(@"子线程3即将结束");
}

八、线程锁

当多个线程同时操作同一个资源的时候,这时如果不处理,那么这个资源有可能就会紊乱,达不到我们想要的效果,所以如果我们要保证同时访问的重要数据不紊乱,我们需要添加线程锁,阻塞线程,使线程同步。排队访问

- (void)viewDidLoad {
    [super viewDidLoad];
    //必须要先创建锁
    _lock = [[NSLock alloc] init];
    //创建两个线程 操作同一个资源变量
    [NSThread detachNewThreadSelector:@selector(thread1Click:) toTarget:self withObject:@"线程1"];
    [NSThread detachNewThreadSelector:@selector(thread2Click:) toTarget:self withObject:@"线程2"];   
}
- (void)thread1Click:(id)obj {
#if 0
    [_lock lock];//加锁  
    NSLog(@"thread1开始");
    for (NSInteger i = 0 ; i < 10; i++) {
        _cnt += 2;//想让 _cnt 连续+2
        NSLog(@"thread1_cnt:%ld",_cnt);
        [NSThread sleepForTimeInterval:0.2];
    }
    NSLog(@"thread1即将结束");   
    [_lock unlock];//解锁  
    //访问资源结束解锁
#else
    // @synchronized (self){} 类似于 加锁和解锁过程 
    @synchronized(self) { 
    //使线程对当前对象进行操作时,同步进行,阻塞线程
    //跟加锁原理是一样的,执行 @synchronized(self)会判断有没有加锁,加过锁那么阻塞,没有加锁就继续执行
        NSLog(@"thread1开始");
        for (NSInteger i = 0 ; i < 10; i++) {
            _cnt += 2;//想让 _cnt 连续+2
            NSLog(@"thread1_cnt:%ld",_cnt);
            [NSThread sleepForTimeInterval:0.2];
        }
        NSLog(@"thread1即将结束");
    }
#endif
}
- (void)thread2Click:(id)obj {
    [_lock lock];
    NSLog(@"thread2开始");
    for (NSInteger i = 0 ; i < 10; i++) {
        _cnt -= 5;//让 _cnt连续-5
        NSLog(@"thread2_cnt:%ld",_cnt);
        [NSThread sleepForTimeInterval:0.2];
    }
    NSLog(@"thread2即将结束");
    [_lock unlock];
}

九、任务队列

NSThread操作线程是最基本的类,NSOperation是一个轻量级的线程,任务队列得到的子线程的效率要高于NSTread。

NSOperation是以任务为导向的管理线程机制,将操作(任务)放入到线程池里,会自动执行,弱化线程的概念(任务:可以简单的理解为线程)

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建一个线程池
    _queue = [[NSOperationQueue alloc] init];
    //设置 一个队列中 允许 最大 任务的并发 个数
    _queue.maxConcurrentOperationCount = 2;
    //如果写成1  表示 线程池中的任务 一个一个 串行执行  
    [self createInvocationOperation];
    [self createBlockOperation];
}
/*
 NSOperation 是一个抽象类  NSOperation 方法 需要有子类自己实现
 //创建任务对象 都是 NSOperation 的子类对象
 //NSBlockOperation  NSInvocationOperation
 任务 要和 任务队列/线程池 结合使用
 */
#pragma mark - block任务
- (void)createBlockOperation {
    //block代码块就是一个 任务
    NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"block任务:%ld",i);
            [NSThread sleepForTimeInterval:0.3];
        }
    }];  
    [blockOp1 setCompletionBlock:^{
        //block 任务完成之后 会回调 这个block
        NSLog(@"block任务完成");
    }];
    //放在线程池中
    [_queue addOperation:blockOp1];
    
}
#pragma mark - 任务
//第一种任务
- (void)createInvocationOperation {
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation1:) object:@"任务1"];
    //[op1 start]; 任务 默认start 相对于主线程是同步
    //把任务 放入 线程池
    [_queue addOperation:op1];//一旦放入 这个 任务 就会异步启动   
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation2:) object:@"任务2"];
    //把任务 放入 线程池
    [_queue addOperation:op2];    
    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation3:) object:@"任务3"];
    //把任务 放入 线程池
    [_queue addOperation:op3];
}
- (void)operation3:(id)obj {
    NSLog(@"obj:%@",obj);
    for (NSInteger i = 0; i < 10; i++) {
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"op3:i->%ld",i);
    }
    NSLog(@"任务3即将结束");
}
- (void)operation2:(id)obj {
    NSLog(@"obj:%@",obj);
    for (NSInteger i = 0; i < 10; i++) {
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"op2:i->%ld",i);
    }
    NSLog(@"任务2即将结束");
}
- (void)operation1:(id)obj {
    NSLog(@"obj:%@",obj);
    for (NSInteger i = 0; i < 10; i++) {
        [NSThread sleepForTimeInterval:0.5];
        NSLog(@"op1:i->%ld",i);
    }
    NSLog(@"任务1即将结束");
}

十、GCD

GCD 全称Grand Central Dispatch(队列调度),是一套低层API,提供了?种新的方法来进?并发程序编写。从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务,然后提交?至?工作队列来并发地或者串?行地执?行。
GCD是C实现,?NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分,并发任务会像NSOperationQueue那样基于系统负载来合适地并发进?,串?行队列同一时间只执行单一任务
 GCD的API很大程度上基于block

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //[self createMainQueue];
    
    //[self createPrivateQueue];
    
    [self createGlobalQueue];
}
#pragma mark - 全局队列
/*
 3.全局队列
 // 并行队列(全局)不需要我们创建,通过dispatch_get_global_queue()方法获得
 // 三个可用队列
 // 第一个参数是选取按个全局队列,一般采用DEFAULT,默认优先级队列
 // 第二个参数是保留标志,目前的版本没有任何用处(不代表以后版本),直接设置为0就可以了
 // DISPATCH_QUEUE_PRIORITY_HIGH
 // DISPATCH_QUEUE_PRIORITY_DEFAULT
 // DISPATCH_QUEUE_PRIORITY_LOW
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 */
- (void)createGlobalQueue {
    //全局队列 内部任务 异步/并行 执行
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (NSInteger i = 0; i < 10; i++) {
            [NSThread sleepForTimeInterval:0.5];
            NSLog(@"全局队列任务1:%ld",i);
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (NSInteger i = 0; i < 10; i++) {
            [NSThread sleepForTimeInterval:0.5];
            NSLog(@"全局队列任务2:%ld",i);
        }
    });
}
#pragma mark - 私有队列
/*
 2.创建私有队列 用户队列/串行队列
 // C接口,创建一个私有队列 ,队列名是一个C字符串,没有特别的要求,Apple建议用倒装的标识符来表示(这个名字,更多用于调试)
 私有队列内部也是串行操作
 */
- (void)createPrivateQueue {
    //创建一个私有队列
    //私有队列 相对于主线程 异步
    //私有队列内部的任务 是 串行执行
    //下面函数的第一个参数 就是一个标签字符串 标识队列
    dispatch_queue_t queue = dispatch_queue_create("com.1507", NULL);
    
    //增加任务
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i++) {
            [NSThread sleepForTimeInterval:0.5];
            NSLog(@"私有队列任务1");
        }
    });
    //增加任务 这两个任务 是 ?
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i < 10; i++) {
            [NSThread sleepForTimeInterval:0.5];
            NSLog(@"私有队列任务2");
        }
    });
    //私有队列 相当于 NSOperationQueue 队列 内部最大并发个数是1
}
#pragma mark - 主线程队列
- (void)createMainQueue {
    //主线程队列 只需要获取 --》一般都是在子线程获取 在子线程获取才有意义
    //1.获取主线程队列 -->主线程队列内部 都是 串行
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    //dispatch_async 相对于 当前线程 异步给 queue 增加一个任务
    dispatch_async(queue, ^{
        //给主线程 增加一个任务
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"主线程任务1_i:%ld",i);
            [NSThread sleepForTimeInterval:0.5];
        }
    });
    //增加任务 是异步 的 主线程 队列内部 是串行执行任务的
    dispatch_async(dispatch_get_main_queue(), ^{
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"主线程任务2_i:%ld",i);
            [NSThread sleepForTimeInterval:0.5];
        }
    });
}
时间: 2024-10-14 11:32:15

iOS多线程知识点总结的相关文章

iOS 常见知识点(三):Lock

iOS 常见知识点(一):Runtime iOS 常见知识点(二):RunLoop 锁是最常用的同步工具.一段代码段在同一个时间只能允许被有限个线程访问,比如一个线程 A 进入需要保护代码之前添加简单的互斥锁,另一个线程 B 就无法访问,只有等待前一个线程 A 执行完被保护的代码后解锁,B 线程才能访问被保护代码. iOS 中的八大锁 NSLock @protocol NSLocking - (void)lock; - (void)unlock; @end @interface NSLock :

iOS多线程开发之NSOperation - 快上车,没时间解释了!

一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强,很屌的是加入了操作依赖. 默认情况下,NSOperation单独使用时只能同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行.讲到这里,我们不难发现GCD和NSOperation实现的方式很像,其实这更像是废话,NSOperation本身就是基于GCD的

iOS多线程 GCD

iOS多线程 GCD Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取. /*! * @function dispatch_get_main_queue * * @abstract * Returns the default queue that is bound to the main thread. *

iOS多线程编程

1. 进程,线程, 任务 进程:一个程序在运行时,系统会为其分配一个进程,用以管理他的一些资源. 线程:进程内所包含的一个或多个执行单元称为线程,线程一般情况下不持有资源,但可以使用其所在进程的资源. 任务:进程或线程中要做的事情. 在引入线程的操作系统中,通常把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位. 线程比进程更小,对其调度的开销小,能够提高系统内多个任务的并发执行程度. 一个程序至少有一个进程,一个进程至少有一个线程.一个程序就是一个进程,而一个程序中的多个任

关于iOS多线程的总结

关于iOS多线程的总结 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项.当然也会给出几种多线程的案例,在实际使用中感受它们的区别.还有一点需要说明的是,这篇文章将会使用 Swift 和 Objective-c 两种语言讲解,双语幼儿园.OK,let's begin! 概述 这篇文章中,我不会说多线程是什么.线程和进程的区别.多线程有什么用,当然我也不会说什么是串行.什么是并行等问题,这些我们应该都知道的. 在 iOS 中其实目前有 4 套多线程方案,他们分

线程同步-iOS多线程编程指南(四)-08-多线程

首页 编程指南 Grand Central Dispatch 基本概念 多核心的性能 Dispatch Sources 完结 外传:dispatch_once(上) Block非官方编程指南 基础 内存管理 揭开神秘面纱(上) 揭开神秘面纱(下) iOS多线程编程指南 关于多线程编程 线程管理 Run Loop 线程同步 附录 Core Animation编程指南 Core Animation简介 基本概念 渲染架构 几何变换 查看目录 中文手册/API ASIHTTPRequest Openg

iOS多线程的初步研究(一)-- NSThread

iOS多线程的初步研究(一)-- NSThread 对于多线程的开发,iOS系统提供了多种不同的接口,先谈谈iOS多线程最基础方面的使用.产生线程的方式姑且分两类,一类是显式调用,另一类是隐式调用. 一.显示调用的类为NSThread.一般构造NSThread的线程对象可通过两种方式: 1. 初始化线程主方法: [NSThread detachNewThreadSelector:@selector(run:) toTarget:target withObject:obj];//类方法 或 NST

iOS 多线程详解

iOS开发 多线程 概览 机器码是按顺序执行的,一个复杂的多步操作只能一步步按顺序逐个执行.改变这种状况可以从两个角度出发: 对于单核处理器,可以将多个步骤放到不同的线程,这样一来用户完成UI操作后其他后续任务在其他线程中,当CPU空闲时会继续执行,而此时对于用户而言可以继续进行其他操作: 对于多核处理器,如果用户在UI线程中完成某个操作之后,其他后续操作在别的线程中继续执行,用户同样可以继续进行其他UI操作,与此同时前一个操作的后续任务可以分散到多个空闲CPU中继续执行(当然具体调度顺序要根据

iOS多线程GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取. /*! * @function dispatch_get_main_queue * * @abstract * Returns the default queue that is bound to the main thread. * * @discussi