swift开发多线程篇 - 多线程基础
swift开发多线程篇 - 多线程基础
iOS 的三种多线程技术
- (1)NSThread
- 使用NSThread对象建立一个线程非常方便
- 但是!要使用NSThread管理多个线程非常困难,不推荐使用
- 技巧!使用[NSThread currentThread]跟踪任务所在线程,适用于这三种技术
- (2) NSOperation/NSOperationQueue
- 是使用GCD实现的一套Objective-C的API
- 是面向对象的线程技术
- 提供了一些在GCD中不容易实现的特性,如:限制最大并发数量、操作之间的依赖关系
- (3) GCD —— Grand Central Dispatch
- 是基于C语言的底层API
- 用Block定义任务,使用起来非常灵活便捷
- 提供了更多的控制能力以及操作队列中所不能使用的底层函数
CGD基本思想
- GCD的基本思想是就将操作s放在队列s中去执行
- 操作使用Blocks定义
- 队列负责调度任务执行所在的线程以及具体的执行时间
- 队列的特点是先进先出(FIFO)的,新添加至对列的操作都会排在队尾
- 提示
- GCD的函数都是以dispatch(分派、调度)开头的
- 队列
- dispatch_queue_t
- 串行队列,队列中的任务只会顺序执行
- 并行队列,队列中的任务通常会并发执行
- 操作
- dispatch_async异步操作,会并发执行,无法确定任务的执行顺序
- dispatch_sync 同步操作,会依次顺序执行,能够决定任务的执行顺序
串行队列
//dispatch_queue 是队列名称,在调试时辅助
var q =dispatch_queue_create("lllll",DISPATCH_QUEUE_SERIAL) //SERIAL 代表串行
dispatch_sync(q) { //sync 是同步
print("串行同步 %@", [NSThread.currentThread()]//同步操作不会新建线程、操作顺序执行(没用!)
}
dispatch_async(q) { //async 是异步
print("串行异步 %@", [NSThread.currentThread()]) //异步操作会新建线程、操作顺序执行(非常有 用!)场景:既不影响主线程,又需要顺序执行的操作!
}
并行队列
var q =dispatch_queue_create("lllll",DISPATCH_QUEUE_CONCURRENT) //CONCURRENT 代表并行
dispatch_sync(q) { //sync 是同步
print("并行同步 %@", [NSThread.currentThread()] //同步操作不会新建线程、操作顺序执行
}
dispatch_async(q) { //async 是异步
print("并行异步 %@", [NSThread.currentThread()]) //异步操作会新建多个线程、操作无序执行(有用,容易出错!)队列前如果有其他任务,会等待前面的任务完成之后再执行场景:既不影响主线程,又不需要顺序执行的操作!
}
调整顺序再运行
全局队列
var q =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0) //全局队列是系统的,直接拿过来(GET)用就可以与并行队列类似,但调试时,无法确认操作所在队列
dispatch_sync(q) { //sync 是同步
print("全局同步 %@", [NSThread.currentThread(),i] //同步操作不会新建线程、操作顺序执行
}
dispatch_async(q) { //async 是异步
print("全局异步 %@", [NSThread.currentThread()],i) //会新建多个线程、操作无序执行队列前如果有其他任务,会等待前面的任务完成之后再执行
}
主队列
var q =dispatch_get_main_queue() //每一个应用程序对应唯一一个主队列,直接GET即可在多线程开发中,使用主队列更新UI
dispatch_sync(q) {
print("主队列同步 %@", [NSThread.currentThread()]) //如果把主线程中的操作看成一个大的Block,那么除非主线程被用户杀掉,否则永远不会结束主队列中添加的同步操作永远不会被执行,会死锁
}
dispatch_async(q) {
print("主队列异步 %@", [NSThread.currentThread()]) //主队列中的操作都应该在主线程上顺序执行的,不存在异步的概念
}
不同队列中嵌套dispatch_sync的结果
// 全局队列,都在主线程上执行,不会死锁
var q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
// 并行队列,都在主线程上执行,不会死锁
var q = dispatch_queue_create("lllll", DISPATCH_QUEUE_CONCURRENT)
// 串行队列,会死锁,但是会执行嵌套同步操作之前的代码
var q = dispatch_queue_create("lllll", DISPATCH_QUEUE_SERIAL)
// 直接死锁
var q = dispatch_get_main_queue()
dispatch_sync(q) {
print("同步任务 %@", [NSThread.currentThread()])
dispatch_sync(q) {
print("同步任务 %@", [NSThread.currentThread()])
}
}
串行队列,同步任务,不需要新建线程
串行队列,异步任务,需要一个子线程,线程的创建和回收不需要程序员参与!
“是最安全的一个选择”串行队列只能创建!
并行队列,同步任务,不需要创建线程
并行队列,异步任务,有多少个任务,就开N个线程执行,
无论什么队列和什么任务,线程的创建和回收不需要程序员参与。
线程的创建回收工作是由队列负责的
“并发”编程,为了让程序员从负责的线程控制中解脱出来!只需要面对队列和任务!
GCD阶段性小结
- GCD
- 通过GCD,开发者不用再直接跟线程打交道,只需要向队列中添加代码块即可
- GCD在后端管理着一个线程池,GCD不仅决定着代码块将在哪个线程被执行,它还根据可用的系统资源对这些线程进行管理。从而让开发者从线程管理的工作中解放出来,通过集中的管理线程,缓解大量线程被创建的问题
- 使用GCD,开发者可以将工作考虑为一个队列,而不是一堆线程,这种并行的抽象模型更容易掌握和使用
- GCD的队列
- GCD公开有5个不同的队列:运行在主线程中的主队列,3 个不同优先级的后台队列,以及一个优先级更低的后台队列(用于 I/O)
- 自定义队列:串行和并行队列。自定义队列非常强大,建议在开发中使用。在自定义队列中被调度的所有Block最终都将被放入到系统的全局队列中和线程池中
- 提示:不建议使用不同优先级的队列,因为如果设计不当,可能会出现优先级反转,即低优先级的操作阻塞高优先级的操作
NSOperation & NSOperationQueue
- 队列及操作
- NSOperationQueue有两种不同类型的队列:主队列和自定义队列
- 主队列运行在主线程上
- 自定义队列在后台执行
- 队列处理的任务是NSOperation的子类
- NSInvocationOperation
- NSBlockOperation
(1)NSOperation基本使用步骤:
- 定义操作队列
- 定义操作
- 将操作添加到队列
- 提示:一旦将操作添加到队列,操作就会立即被调度执行
(2)NSOperationOperation(调度操作)
- 定义队列
self.myQueue = [[NSOperationQueue alloc] init];
- 操作调用的方法
- (void)operationAction:(id)obj
{
NSLog(@"%@ - obj : %@", [NSThread currentThread], obj);
}
- 定义操作并添加到队列
NSInvocationOperation *op = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationAction:) object:@(i)];
[self.myQueueaddOperation:op];
- 小结:需要准备一个被调度的方法,并且能够接收一个参数
(3)NSBlockOperation(块操作)
- 定义操作并添加到队列
NSBlockOperation *op = [NSBlockOperationblockOperationWithBlock:^{
[self operationAction:@"Block Operation"];
}];
- 将操作添加到队列
[self.myQueue addOperation:op];
- 小结:NSBlockOperation比NSInvocationOperation更加灵活
(4)设置同事并发的线程数量
[self.myQueue setMaxConcurrentOperationCount:2];//红色字体代表设置同时并发的线程数量能够有效地降低CPU和内存的开销这 一功能用GCD不容易实现
for (int i = 0; i < 10; ++i) {
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[self operationAction:@(i)];
}];
[self.myQueue addOperation:op];
}
- 问题
块代码中的self为什么不会造成循环引用?
- AFN,底层用GCD开发,开发的接口是NSOperation的
多线程中的循环引用
- 如果self对象持有操作对象的引用,同时操作对象当中又直接访问了self时,才会造成循环引用
- 单纯在操作对象中使用self不会造成循环引用
- 注意:此时不能使用(weakSelf)
(1)多线程中的资源共享
- 并发编程中许多问题的根源就是在多线程中访问共享资源。资源可以是一个属性、一个对象、网络设备或者一个文件等
- 在多线程中任何一个共享的资源都可能是一个潜在的冲突点,必须精心设计以防止这种冲突的发生
(2)互斥锁(@synchronized)
单例和单例的实现步骤
单例:
- 通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源
- 如果希望系统中某个类的对象只能存在一个,单例模式是最好的解决方案
- iOS中最常见的单例就是UIApplication
- 应用场景:
- 音频播放,背景音乐!
- 硬件资源:加速器、[UIScreen mainScreen]
- sharedXX, mainXXX
- 实现步骤:
- 重写allocWithZone方法
- allocWithZone方法是对象分配内存空间时,最终会调用的方法,重写该方法,保证只会分配一个内存空间
- 建立sharedXXX类方法,便于其他类访问
+ (id)allocWithZone:(struct _NSZone *)zone
{
static Ticket *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
} //dispatch_once 是指线程安全的能够做到在多线程的环境下Block中的代码只会被执行一次
- 单例的优点与缺点
优点:可以阻止其他对象实例化单例对象的副本,从而确保所有对象都访问唯一实例
缺点:单例对象一旦建立,对象指针是保存在静态区的,单例对象在堆中分配的内存空间,会在应用程序终止后才会被释放
提示:只有确实需要唯一使用的对象才需要考虑单例模式,不要滥用单例
NSObject的多线程方法
- 开启后台执行任务的方法
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
- 在后台线程中通知主线程执行任务的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
- 获取线程信息
[NSThreadcurrentThread]
- 线程休眠
[NSThreadsleepForTimeInterval:2.0f];
- 特点
- 使用简单,量级轻
- 不能控制线程的数量以及执行顺序
NSObject的多线程方法注意事项
- NSObject的多线程方法使用的是NSThread的多线程技术
- 而NSThread的多线程技术不会自动使用@autoreleasepool
- 在使用NSObject或NSThread的多线程技术时,如果涉及到对象分配,需要手动添加@autoreleasepool
@autoreleasepool
- 自动释放池的工作原理
- 标记为autorelease的对象在出了作用域范围后,会被添加到最近一次创建的自动释放池中
- 当自动释放池被销毁或耗尽时,会向自动释放池中的所有对象发送release消息
- 每个线程都需要有@autoreleasepool,否则可能会出现内存泄漏,但是使用NSThread多线程技术,并不会为后台线程创建自动释放池
自动释放池常见面试代码
for (int i =0; i < 10; ++i) {
NSString *str =@"Hello World";
str = [str stringByAppendingFormat:@" - %d", i];
str = [str uppercaseString];
NSLog(@"%@", str);
}
swift开发多线程篇 - 多线程基础的更多相关文章
- swift开发网络篇 - 网络基础
GET & POST GET GET的语义是获取指定URL的资源 将数据按照variable=value的形式,添加到action所指向的URL后面,并且两者使用"?"连接 ...
- iOS开发多线程篇—多线程简单介绍
iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcod ...
- iOS开发网络篇—多线程断点下载
iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始, ...
- iOS开发多线程篇—多线程简介
iOS开发多线程篇-多线程简介 一.进程和线程 1.什么是进程 进程是指在系统中正在执行的一个应用程序 每一个进程之间是独立的.每一个进程均执行在其专用且受保护的内存空间内 比方同一时候打开QQ.Xc ...
- iOS开发UI篇—Button基础
iOS开发UI篇—Button基础 一.简单说明 一般情况下,点击某个控件后,会做出相应反应的都是按钮 按钮的功能比较多,既能显示文字,又能显示图片,还能随时调整内部图片和文字的位置 二.按钮的三种状 ...
- swift开发网络篇—NSURLConnection基本使用
iOS开发网络篇—NSURLConnection基本使用 一.NSURLConnection的常用类 (1)NSURL:请求地址 (2)NSURLRequest:封装一个请求,保存发给服务器的全部数据 ...
- iOS开发——多线程篇——多线程介绍
一.进程和线程1.什么是进程进程是指在系统中正在运行的一个应用程序每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开迅雷.Xcode,系统就会分别启动2个进程 通过“活动监 ...
- iOS开发——高级篇——多线程的安全隐患
资源共享 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源 比如多个线程访问同一个对象.同一个变量.同一个文件 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题 一.解 ...
- iOS开发——高级篇——多线程GCD死锁
面试题 请问以下代码打印结果: - (void)interview01 { // 以下代码是在主线程执行的 NSLog(@"执行任务1"); dispatch_queue_t qu ...
随机推荐
- Java实时读取日志文件
古怪的需求 在实习的公司碰到一个古怪的需求:在一台服务器上写日志文件,每当日志文件写到一定大小时,比如是1G,会将这个日志文件改名成另一个名字,并新建一个与原文件名相同的日志文件,再往这个新建的日志文 ...
- Ubuntu Server 16.04下ASP.NET Core Web Api + MySql + Dapper在 Jexus、nginx 下的简单测试
一.环境及工具 1.服务器 VirtualBox5.1.4 安装 Ubuntu Server 16.04 amd64 MySql Ver 14.14 Distrib 5.6.21 Jexus 5.8. ...
- WinPhone学习笔记(五)——LongListSelector
LongListSelector也是WinPhone的特色控件之一,最初不了解这个控件叫啥名,知道它会在"人脉"里面出现,在应用程序列表也是这个LongListSelector(如 ...
- 【吐槽】IM群里几种我认为愚蠢的提问方式
一.“有人吗?” 你能得到一句[在,请说]的答复我就服了你,这样问的结果往往是等半天没一个人鸟你,悲观的你或者就此凄凉的退群了,感概人情冷暖的同时甚至开始怀疑人生:积极的你或者这才意识到~要不干脆说问 ...
- Windows下查看JDK是否安装以及安装路径
查看JDK是否已经安装,可以在cmd窗口里输入java -version,如果没有提示出错,就表示已经安装. 查看JDK的安装路径,可以输入java -verbose,会返回很多信息,其中就包含了JD ...
- Delphi String 常用字串符处理函数
Delphi 在面对跨平台开发,程序语言也改进不少,不过有些改进,让原本 Delphi 开发者有些不适应,最显注的就是字串处理函数了,原本 Pascal 语言字串起始由 1 开始,几乎是它的经典了,新 ...
- Firemonkey Bitmap 设定像素颜色 Pixel
VCL 和 Firemonkey 的 Bitmap 处理像素的方式不相同,下例为将图片内不是「白色」的像素全部改成「黑色」: procedure TForm1.Button1Click(Sender: ...
- 程序员下一门要学的编程语言Swift
基于PHP是世界上最好的编程语言这个真理,我一直认为Hack才是程序员要学的下一门编程语言. 但今天看到InfoQ放出的新闻:"Google或许会将Swift编程语言纳入Android平台并 ...
- 最新微信公众平台js sdk整合PHP版
由于没有持续关注微信公众平台相关的开发,所以看到这个东西时,都没有耐心看完开发文档,或者不知道重点. 重点在哪呢?重点在示例代码:http://mp.weixin.qq.com/wiki/7/aaa1 ...
- mysql和oracle jdbc连接
加载驱动. Class.forName("oracle.jdbc.driver.OracleDriver"); 1 创建连接. Connection con = DriverMan ...