OC中多线程的使用、概念、创建方式、生命周期、使用注意等
来源:互联网 发布:东南大学没落 知乎 编辑:程序博客网 时间:2024/05/29 13:02
进程
什么是进程:在我们的系统中正在运行的程序
进程的作用:负责给应用程序分配内存空间(该空间是受保护的,独立的)
线程
什么是线程:线程是CPU调度的最小单元,由CPU调度
线程的作用:负责执行应用程序中的代码,在系统中运行着的程序的代码只能由线程执行
线程创建过程:应用程序在启动过程中,系统会自动创建默认的线程,也就是程序的主线程/UI线程
线程与进程之间的关系:一个进程至少有一个线程(即主线程),一个进程中可以有多个线程
主线程的主要作用
a、显示\刷新UI界面
b、处理UI事件(比如点击事件、滚动事件、拖拽事件等)
主线程相关用法
+ (NSThread *)mainThread; //获得主线程
- (BOOL)isMainThread; //是否为主线程
+ (BOOL)isMainThread; //是否为主线程
开发原则:1、比较耗时的操作都放到子线程中(一般是在进行网络请求的时候,或者是执行时间不可控的时候)
2、UI操作、与用户交互的代码都放到主线程中,其一是因为为了保证用户操作的流程性,其二是因为所有的UI控件都在UIKIT框架中,而UIKIT框架采用的就是这种机制,苹果公司也推荐这种用法,都是非线程安全的,为了保证正确,将所有的用户交互的代码放到主线程中,而单一的线程是按顺序执行的,所以就避免了非线程安全的问题
线程内的代码的执行顺序:
串行执行:同一线程中,该线程中的任务代码按顺序执行,同一时间只能执行一块代码
并发执行:一个进程可以创建多个子线程,在不同的线程中,任务同时执行(其实是一种假象,是因为CPU的调度速度非常快速,所以感觉是一起执行的)(即多线程)
并行执行:真正的同时执行,由多个CPU同时执行
多线程的优点
a、能适当提高程序的执行效率
b、能适当提高资源利用率(CPU、内存利用率)
多线程的缺点
a、开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
b、线程越多,CPU在调度线程上的开销就越大
c'程序设计更加复杂:比如线程之间的通信、多线程的数据共享
线程安全:保证多条线程进行读写操作,都能够得到正确的结果!
使用线程同步技术
解决方案:互斥锁
优点:能有效防止因多线程抢夺资源而引起的数据安全问题!
缺点:需要消耗大量的CPU资源!
原理:多条线程在同一条线上按顺序执行任务!
iOS中多线程的实现方案
方案1:pthread:C
一套通用的多线程API、适用于Unix\Linux\Windows等系统、跨平台\可移植、使用难度大
//创建线程:返回值: 0 == 创建成功,!= 0 返回的是失败的状态码 // pthread_t *restrict :线程标识符的地址,标识一条线程。 // const pthread_attr_t *restrict: 给线程设置属性。 // <#void *(*)(void *)#>: 要执行的方法 // <#void *restrict#> : 传递给执行函数的参数 int result = pthread_create(&mythread, NULL, longTimeOperation, str);
方案2:NSThread:OC
使用更加面向对象、简单易用,可直接操作线程对象
创建、启动线程,initWithTarget:在哪里执行,object:参数 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; 就会告诉 CPU 准备就绪,可以随时接受 CPU 调度! CPU 调度当前线程之后,就会在线程thread中执行self的run方法 [thread start]; NSThread *current = [NSThread currentThread]; 线程的调度优先级 + (double)threadPriority; + (BOOL)setThreadPriority:(double)p; - (double)threadPriority; - (BOOL)setThreadPriority:(double)p; 调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高 线程的名字 - (void)setName:(NSString *)n; - (NSString *)name; 第二种创建方式:创建线程后自动启动线程 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; 第三种创建方式:隐式创建并启动线程 [self performSelectorInBackground:@selector(run) withObject:nil]; 上述2种创建线程方式的优缺点 优点:简单快捷 缺点:无法对线程进行更详细的设置 阻塞(暂停)线程 + (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; // 进入阻塞状态 强制停止线程 + (void)exit; // 进入死亡状态
<span style="font-size:18px;">/* 线程状态 线程池:存放线程的池子! 分为: 可调度线程池: CPU 只会调度可调度线程池中的线程! 下面蓝色状态都位于可调度线程池中! '就绪' ,'运行'! 不可调度线程池: 下面红色状态都位于不可调度线程池中! "新建" ,"阻塞" ,"死亡"! 线程状态: start CPU调度当前线程 运行结束/强制退出(exit) "新建" ---------->'就绪' -----------------> '运行' -----------------------> "死亡"; CPU 调度其他线程 CPU调度当前线程 '运行' ------------------> '就绪'-----------------> '运行' 调用 sleep/等待互斥锁 sleep时间到/得到互斥锁 '运行' -----------------------> "阻塞"-----------------------> '就绪'; 线程运行结束或者强制退出(exit)就进入 "死亡" 状态; "注意:一旦线程停止(死亡),就不可以再次开启任务!程序会挂掉: Crash! 面试语句: 平时开发中,要特别关注 Crash! :"PO"级别的 "Bug"; 注意:一旦线程停止(死亡)了,就不能再次开启任务 线程间通信 什么叫做线程间通信 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信 线程间通信的体现 1个线程传递数据给另1个线程 在1个线程中执行完特定任务后,转到另1个线程继续执行任务 线程间通信常用方法 - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait; */</span><pre name="code" class="html"><span style="white-space:pre"></span>
<pre name="code" class="html">//屏幕点击的时候触发,线程间的通信演示-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ NSLog(@"touchesBegan:%@",[NSThread currentThread]); //创建一个线程对象 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(didDownLoadImage) object:nil]; //设置线程名称,一般用于调试 thread.name = @"我是自定义的子线程"; //设置线程大小 //thread.stackSize = 1024 * 1; //将线程加入到可执行线程池中,CPU随时可以调用 [thread start];}//线程方法-(void)didDownLoadImage{ NSLog(@"didDownLoadImage:%@",[NSThread currentThread]); //网络资源唯一标示符, NSURL *url = [[NSURL alloc] initWithString:@"http://e.hiphotos.baidu.com/image/pic/item/11385343fbf2b211e7d75bf9cf8065380dd78e88.jpg"]; //获取本地图片 //NSURL *url = [[NSURL alloc] initWithString:@"file:////Users/mac/Desktop/img01.png"]; //将路径转换为二进制对象,获取网络中的资源 NSData *data = [NSData dataWithContentsOfURL:url]; //将图片保存到本地 [data writeToFile:@"/Users/mac/Desktop/多线程/image/mv01.jpg" atomically:YES]; //将二进制文件转换为图片 UIImage *image = [UIImage imageWithData:data]; //图片下载成功后,返回主线程显示图片 [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];}//显示图片-(void)showImage:(UIImage*)image{ NSLog(@"showImage:%@",[NSThread currentThread]); //设置图片 self.gsImage.image = image;}
<span style="font-size:18px;">//// ViewController.m// 可变化的宫格//// Created by mac on 15/9/1.// Copyright (c) 2015年 superMac. All rights reserved.//#import "ViewController.h"#define magin 4#define count 20#define cell 4#define row (count / cell + (count % cell > 0 ? 1 : 0))#define gsScreenWidth [[UIScreen mainScreen] bounds].size.width#define gsScreenHeight [[UIScreen mainScreen] bounds].size.height#define gsHeight (gsScreenWidth - (magin * (row + 1))) / row + 50#define gsWidth (gsScreenWidth - (magin * (cell + 1))) / cell@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; [self showImage];}-(void)showImage{ for (int i = 0; i < count; i++) { //行的下标 CGFloat rowIndex = i / cell; //列的下标 CGFloat cellIndex = i % cell; //x CGFloat x = (magin + gsWidth ) * cellIndex + magin; //y CGFloat y = (magin + gsHeight) * rowIndex + magin; //创建imageView UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"mv01.jpg"]]; imageView.frame = CGRectMake(x, y, gsWidth, gsHeight); [self.view addSubview:imageView]; }}@end</span>
方案3:GCD:C
旨在替代NSThread等线程技术、充分利用设备的多核
方案4:NSOperation:OC
基于GCD(底层是GCD)、比GCD多了一些更简单实用的功能、使用更加面向对象
补充
添加桥接的目的
凡是函数名中带有create\copy\new\retain等字眼,都应该在不需要使用这个数据的时候进行release
GCD的数据类型在ARC环境下不需要再做release
CF(Core Foundation)的数据类型在ARC\MRC环境下都需要再做release
安全隐患
资源共享
1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
比如多个线程访问同一个对象、同一个变量、同一个文件
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
安全隐患解决 –互斥锁
互斥锁使用格式
@synchronized(锁对象) { //需要锁定的代码 }
注意:锁定1份代码只用1把锁,用多把锁是无效的
互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
互斥锁的使用前提:多条线程抢夺同一块资源
相关专业术语:线程同步
线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
互斥锁,就是使用了线程同步技术
原子和非原子属性
OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁
nonatomic和atomic对比
atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,适合内存小的移动设备
iOS开发的建议
所有属性都声明为nonatomic
尽量避免多线程抢夺同一块资源
尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
C语言中的 void * 就等同于 OC 中的 id;
-fobjc-arc :可以让旧项目支持arc
-fno-objc-arc :让原来支持arc的文件不使用arc
- OC中多线程的使用、概念、创建方式、生命周期、使用注意等
- OC中6种多线程的使用方式
- oc中alloc和init等基本注意和使用
- OC中block的使用及注意
- OC学习笔记10--Block的概念与使用方式
- 创建使用多线程的 三种方式
- OC中多线程的一些概念
- OC中多线程的创建方法
- 多线程的使用注意点
- Java中使用匿名内部类创建多线程的3种方式
- vue 中使用button等表单元素的注意点
- 多线程的概念和使用
- OC中类和对象的创建和使用
- java Vector 在多线程使用中需要注意的问题
- Java进阶(四十二)Java中多线程使用匿名内部类的方式进行创建3种方式
- Oracle中创建同义词(注意引号的使用)
- OC多态使用注意
- OC中关于copy和mutableCopy的使用 及深拷贝、浅拷贝的概念
- 三阶贝塞尔曲线拟合圆弧的一般公式
- 给python交互式命令行增加自动补全和命令历史
- 自学Java系列 笔记4 多线程 1
- 新手dp
- 自学Java系列 笔记4 多线程 2
- OC中多线程的使用、概念、创建方式、生命周期、使用注意等
- [笔记][Java7并发编程实战手册]4.9-4.10在执行器中控制任务的完成和取消任务FutureTask
- Poj 3210 Coins(推理)
- 自学Java系列 笔记4 线程安全
- mpstat
- android目录结构
- 文件操作
- Python 异常处理
- 自学Java系列 笔记5 Java学习之HashMap和Hashtable的区别