iOS Principle:Thread(上)

简介: iOS Principle:Thread(上)

方便记忆:


  • 进程和线程:进程是饭馆、线程是工作人员
  • 线程的执行:1个线程中任务的执行是串行的
  • 多线程并发执行:CPU快速地在多条线程之间调度
  • iOS 多线程
  • pthread:c语言的通用api,跨平台、难度大、非自动管理生命周期
  • NSThread:面向对象的非自动管理生命周期
  • GCD:利用设备的多核自动管理生命周期
  • NSOperation、NSOperationQueue:封装GCD,加强依赖关系、监控状态、并发管理
  • 常用安全锁:@synchronized{}、[self.lock lock]


一.相关概念


1.1 进程

进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。


1.2 线程

线程和进程的关系

1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。

线程内部是串行执行的?

1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。


1.3 多线程

即1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。比如同时开启3条线程分别下载3个文件(分别是文件A、文件B、文件C)。

多线程并发执行的原理?

在同一时间里,CPU只能处理1条线程,只有1条线程在工作(执行)。多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

多线程的优缺点?

优点:

  • 1)能适当提高程序的执行效率。
  • 2)能适当提高资源利用率(CPU、内存利用率)

缺点:

  • 1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
  • 2)线程越多,CPU在调度线程上的开销就越大。
  • 3)程序设计更加复杂:比如线程之间的通信、多线程的数据共享



1.4 多线程在 iOS 开发中的应用

iOS 的主线程?

一个 iOS 程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”,用于刷新显示UI,处理UI事件。

不要将耗时操作放到主线程中去处理,会卡住线程。

iOS 的多线程?

1)第一种:pthread

特点:

  • 一套通用的多线程API
  • 适用于Unix\Linux\Windows等系统
  • 跨平台\可移植
  • 使用难度大

使用语言:c语言

使用频率:几乎不用

线程生命周期:由程序员进行管理

2)第二种:NSThread

特点:

  • 使用更加面向对象
  • 简单易用,可直接操作线程对象

使用语言:OC语言

使用频率:偶尔使用

线程生命周期:由程序员进行管理

3)第三种:GCD

特点:

  • 旨在替代NSThread等线程技术
  • 充分利用设备的多核(自动)

使用语言:OC语言

使用频率:经常使用

线程生命周期:自动管理

4)第四种:NSOperation

特点:

  • 基于GCD(底层是GCD)
  • 比GCD多了一些更简单实用的功能
  • 使用更加面向对象

对比 GCD 的优点:

  • 在NSOperationQueue中,可以建立各个NSOperation之间的依赖关系
  • 有KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
  • NSOperationQueue可以方便的管理并发、NSOperation之间的优先级

使用语言:OC语言

使用频率:经常使用

线程生命周期:自动管理


二.代码示例


由于要使用一些 c 来演示,先说明些规范,防懵逼 😳

  • 1.在c语言中,没有对象的概念,对象类型是以-t/Ref结尾的;
  • 2.c语言中的void * 和OC的id是等价的;
  • 3.在混合开发时,如果在 C 和 OC 之间传递数据,需要使用 __bridge 进行桥接,桥接的目的就是为了告诉编译器如何管理内存,MRC 中不需要使用桥接;
  • 4.在 OC 中,如果是 ARC 开发,编译器会在编译时,根据代码结构, 自动添加 retain/release/autorelease。但是,ARC 只负责管理 OC 部分的内存管理,而不负责 C 语言 代码的内存管理。因此,开发过程中,如果使用的 C 语言框架出现retain/create/copy/new 等字样的函数,大多都需要 release,否则会出现内存泄漏。


2.1 pthread 基本使用

需要引入 pthread 头文件

#import <pthread.h>

使用 pthread_create 方法

/**
pthread_create(<#pthread_t  _Nullable *restrict _Nonnull#>, <#const pthread_attr_t *restrict _Nullable#>, <#void * _Nullable (* _Nonnull)(void * _Nullable)#>, <#void *restrict _Nullable#>)
参数:
1.指向线程标识符的指针,C 语言中类型的结尾通常 _t/Ref,而且不需要使用 *;
2.用来设置线程属性;
3.指向函数的指针,传入函数名(函数的地址),线程要执行的函数/任务;
4.运行函数的参数;
*/

完整代码

{
    //1.创建线程对象
    pthread_t thread;
    //2.创建线程
    NSString *param = @"参数";
    int result = pthread_create(&thread, NULL, task, (__bridge void *)(param));
    result == 0?NSLog(@"success"):NSLog(@"failure");
    //3.设置子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源,或者在子线程中添加 pthread_detach(pthread_self()),其中pthread_self()是获得线程自身的id
    pthread_detach(thread);
}
void *task(void * param) {
    //在此做耗时操作
    NSLog(@"new thread : %@  参数是: %@",[NSThread currentThread],(__bridge NSString *)(param));
    return NULL;
}


2.2 NSThread 基本使用

1.1)第一种 创建线程的方式:alloc init.

特点:需要手动开启线程,可以拿到线程对象进行详细设置

  • 第一个参数:目标对象
  • 第二个参数:选择器,线程启动要调用哪个方法
  • 第三个参数:前面方法要接收的参数(最多只能接收一个参数,没有则传nil)
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:@"wendingding"];
//启动线程
[thread start];

1.2)第二种 创建线程的方式:分离出一条子线程

特点:自动启动线程,无法对线程进行更详细的设置

  • 第一个参数:线程启动调用的方法
  • 第二个参数:目标对象
  • 第三个参数:传递给调用方法的参数
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:@"我是分离出来的子线程"];

1.3)第三种 创建线程的方式:后台线程

特点:自动启动县城,无法进行更详细设置

[self performSelectorInBackground:@selector(run) withObject:@"我是后台线程"];

2)常用的控制线程方法

为线程命名,以便区分

//设置线程的名称
thread.name = @"线程A";

设置优先级,取值范围为0.0~1.0之间,1.0表示线程的优先级最高,如果不设置该值,那么理想状态下默认为0.5

//设置线程的优先级
thread.threadPriority = 1.0;

退出当前线程

//退出当前线程
[NSThread exit];

线程的各种状态:新建-就绪-运行-阻塞-死亡,阻塞线程方法来了

[NSThread sleepForTimeInterval:2.0]; //阻塞线程
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; //阻塞线程
//注意:线程死了不能复生

3)线程安全

  • 前提:多个线程访问同一块资源会发生数据安全问题
  • 解决方案:加互斥锁
  • 相关代码:@synchronized(self){}
  • 专业术语-线程同步
  • 原子和非原子属性(是否对setter方法加锁)

死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用。
  • 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  • 不可强行占有:进程已获得的资源,在末使用完之前,不能强行剥夺。
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

产生死锁的原因主要是:

  • 因为系统资源不足。
  • 进程运行推进的顺序不合适。
  • 资源分配不当等。

售票员售票问题

@interface NSThreadSafeViewController ()
//三只售票员🐱🐶🐭
@property (nonatomic, strong) NSThread *thread01;
@property (nonatomic, strong) NSThread *thread02;
@property (nonatomic, strong) NSThread *thread03;
//总票数
@property(nonatomic, assign) NSInteger totalticket;
@end
@implementation NSThreadSafeViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    //假设有10张票
    self.totalticket = 10;
    //创建线程
    self.thread01 =  [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread01.name = @"小🐱";
    self.thread02 =  [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread02.name = @"小🐶";
    self.thread03 =  [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    self.thread03.name = @"小🐭";
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //启动线程
    [self.thread01 start];
    [self.thread02 start];
    [self.thread03 start];
}
//售票
- (void)saleTicket {
    while (1) {
        //2.加互斥锁
        @synchronized(self) {
            [NSThread sleepForTimeInterval:0.03];
            //1.先查看余票数量
            NSInteger count = self.totalticket;
            if (count > 0) {
                self.totalticket = count - 1;
                NSLog(@"%@卖出去了一张票,还剩下%zd张票",[NSThread currentThread].name,self.totalticket);
            } else {
                NSLog(@"%@发现当前票已经买完了--",[NSThread currentThread].name);
                break;
            }
        }
    }
}

运行结果


image.png


4)NSThread 线程间通信(子线程加载图片完成通知主线程更新UI)

- (void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event {
    //开启一条子线程来下载图片
    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
}
- (void)downloadImage {
    //1.确定要下载网络图片的url地址,一个url唯一对应着网络上的一个资源
    NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"];
    //2.根据url地址下载图片数据到本地(二进制数据
    NSData *data = [NSData dataWithContentsOfURL:url];
    //3.把下载到本地的二进制数据转换成图片
    UIImage *image = [UIImage imageWithData:data];
    //4.回到主线程刷新UI
    //4.1 第一种方式
    //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
    //4.2 第二种方式
    //    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
    //4.3 第三种方式
    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
}

5)特殊用法(计算代码段的执行时间)

NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"];
//第一种方法
NSDate *start = [NSDate date];
//2.根据url地址下载图片数据到本地(二进制数据)
NSData *data = [NSData dataWithContentsOfURL:url];
NSDate *end = [NSDate date];
NSLog(@"第二步操作花费的时间为%f",[end timeIntervalSinceDate:start]);
//第二种方法
CFTimeInterval start = CFAbsoluteTimeGetCurrent();
NSData *data = [NSData dataWithContentsOfURL:url];
CFTimeInterval end = CFAbsoluteTimeGetCurrent();
NSLog(@"第二步操作花费的时间为%f",end - start);


目录
相关文章
|
存储 监控 网络协议
iOS 友盟崩溃日志分析——Thread state
iOS 友盟崩溃日志分析——Thread state
617 0
|
iOS开发
iOS Principle:CGAffineTransform
iOS Principle:CGAffineTransform
191 0
iOS Principle:CGAffineTransform
|
安全 Unix API
iOS Principle:CALayer(下)
iOS Principle:CALayer(下)
182 0
iOS Principle:CALayer(下)
|
iOS开发
iOS Principle:CALayer(中)
iOS Principle:CALayer(中)
158 0
iOS Principle:CALayer(中)
|
API C语言 iOS开发
iOS Principle:CALayer(上)
iOS Principle:CALayer(上)
187 0
iOS Principle:CALayer(上)
|
存储 缓存 iOS开发
iOS Principle:weak
iOS Principle:weak
201 0
iOS Principle:weak
|
存储 iOS开发
iOS Principle:Notification(下)
iOS Principle:Notification(下)
125 0
iOS Principle:Notification(下)
|
设计模式 iOS开发
iOS Principle:Notification(上)
iOS Principle:Notification(上)
141 0
iOS Principle:Notification(上)
|
Web App开发 JSON 移动开发
iOS Principle:ReactNative(下)
iOS Principle:ReactNative(下)
198 0
iOS Principle:ReactNative(下)
|
移动开发 前端开发 JavaScript
iOS Principle:ReactNative(中)
iOS Principle:ReactNative(中)
128 0
iOS Principle:ReactNative(中)