生活随笔
收集整理的这篇文章主要介绍了
iOS开发-10.多线程
小编觉得挺不错的,现在分享给大家,帮大家做个参考.
a
) NSThread
/ GCD
/ NSOperation底层都是pthreadb
) NSThread开启线程方式
1 ) 动态实例化
NSThread
* thread
= [ [ NSThread alloc
] initWithTarget
: self selector
: @selector ( test
: ) object
: nil
] ;
thread
. threadPriority
= 1 ;
[ thread start
] ;
2 ) 静态实例化
[ NSThread detachNewThreadSelector
: @selector ( test
: ) toTarget
: self withObject
: nil
] ;
3 ) 隐式实例化
3.1 )
[ self performSelectorOnMainThread
: @selector ( test
: ) withObject
: nil waitUntilDone
: YES
] ;
a
) GCD中有
2 个用来执行任务的函数
1 ) 用同步的方式执行任务
dispatch_sync ( dispatch_queue_t queue
, dispatch_block_t block
) ; 1.1 ) dispatch_sync
: 立马在当前线程同步执行任务
, 不执行就不会往下走
2 ) 用异步的方式执行任务
dispatch_async ( dispatch_queue_t queue
, dispatch_block_t block
) ; 2.1 ) dispatch_async
: 不要求立马在当前线程同步执行任务
, 等上一个任务
( 也可能是整个外部的大函数执行完毕
) 执行完了再执行b
) GCD源码https
:
a
) GCD的队列可以分为
2 大类型
1 ) 并发队列(Concurrent Dispatch Queue)
1.1 ) 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
1.2 ) 并发功能只有在异步(dispatch_async)函数下才有效b
) 串行队列(Serial Dispatch Queue)
1 ) 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
2 ) 主队列是一种特殊的串行队列c
) 队列的特点
: 排队,FIFO,First In First Out 先进先出d
) Global Queue全局队列的指针地址是相同的
, 而手动创建的队列则地址是不相同的
a
) 同步和异步主要影响:能不能开启新的线程
1 ) 同步:在当前线程中执行任务,不具备开启新线程的能力
2 ) 异步:在新的线程中执行任务,具备开启新线程的能力b
) 并发和串行主要影响:任务的执行方式
1 ) 并发:多个任务并发(同时)执行
2 ) 串行:一个任务执行完毕后,再执行下一个任务
a
) 使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)b
) performSelector
: withObject
: afterDelay
: 1 ) 有afterDelay的方法都是跟Runloop有关
, 相当于添加了一个定时器到Runloop去执行 所以你要看当前执行任务的线程有没有Runloop
2 ) 在主线程好时正常调用
, 但是会延迟
, 因为主线程默认就会开启一个Runloop
, 延迟是因为主线程的任务执行顺序是串行的
, 需要等上一个任务执行完毕
, 可以理解为包装该线程调用的
{ } 范围函数调用完毕
, 例如viewDidLoad方法调用完毕
3 ) 但是在子线程却不好使
, 直接不调用
, 因为子线程没有开启Runloop来保活
a
) 思考:如何用gcd实现以下功能
1 ) 异步并发执行任务
1 、任务
2 2 ) 等任务
1 、任务
2 都执行完毕后,再回到主线程执行任务
3
a
) 资源共享
1 ) 1 块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
2 ) 比如多个线程访问同一个对象、同一个变量、同一个文件b
) 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题c
) 多线程安全隐患示例
01 – 存钱取钱
d
) 多线程安全隐患示例
02 – 卖票
e
) 多线程安全隐患分析
f
) 多线程安全隐患的解决方案
1 ) 解决方案:使用线程同步技术
( 同步,就是协同步调,按预定的先后次序进行
) 2 ) 线程同步本质
: 是不能同时让多条线程占用一个资源
2 ) 常见的线程同步技术是
: 加锁
( 所有线程都用同一把锁
) 3 ) 锁的原理
: 首先判断这把锁有没有被人加过
, 没有就会加锁
, 有被加过
, 就会等待
a
) OSSpinLock ( High
- level
- lock高级锁
) 1 ) OSSpinLock叫做”自旋锁”
, 等待锁的线程会处于忙等(busy
- wait)状态
( 相当于执行了一个
do while 循环
) 一直占用着CPU资源
2 ) iOS10
. 0 开始不推荐使用
, 目前已经不再安全,可能会出现优先级反转问题
( 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
, 造成死锁的现象
) 3 ) 需要导入头文件#import
< libkern
/ OSAtomic
. h
>
b
) os_unfair_lock ( Low
- level
- lock低级锁
) 1 ) os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持
2 ) 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
3 ) 需要导入头文件#import
< os
/ lock
. h
>
c
) pthread_mutex ( Low
- level
- lock低级锁
) 1 ) mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
2 ) 需要导入头文件#import
< pthread
. h
>
3 ) pthread_mutex – 递归锁
3.1 ) 递归锁
: 允许用同一条线程对一把锁进行重复加锁
4 ) pthread_mutex – 条件
4.1 ) 条件
( 等待条件
, 等不到条件就休眠
, 等待的时候解锁
, 唤醒的时候加锁
, 次数对等
, 一般用于线程之间的通信
)
d
) NSLock
1 ) NSLock是对mutex普通锁的封装
e
) NSRecursiveLock
1 ) NSRecursiveLock是对mutex递归锁的封装,API跟NSLock基本一致
f
) NSCondition
1 ) NSCondition是对mutex和cond的封装
2 ) signal
: 信号发出的时机
, 决定了后续代码的执行
, 因为在解锁前和解锁后
3 ) signal
: 信号
( 单个
) 和broadcast
: 广播
( 多个
) 的区别
g
) NSConditionLock
1 ) NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值
2 ) 初始化不设置条件值的时候,默认条件值就是
0 3 ) lock
: 直接加锁,不等条件值,如果没有加锁,那就加锁
4 ) lockWhenCondition
: 条件值为多少的时候才加锁
5 ) unlockWithCondition
: 解锁并且设置条件值
6 ) 在子线程执行的任务
, 可以达到依赖的效果
h
) dispatch_semaphore
1 ) semaphore叫做”信号量”
2 ) 信号量的初始值,可以用来控制线程并发访问的最大数量
3 ) 信号量的初始值为
1 ,代表同时只允许
1 条线程访问资源,保证线程同步
i
) dispatch_queue
1 ) 直接使用GCD的串行队列,也是可以实现线程同步的
2 ) 不是说有多线程就需要加锁
j
) @ synchronized
1 ) @ synchronized是对mutex递归锁的封装
2 ) 源码查看:objc4中的objc
- sync
. mm文件
3 ) @ synchronized ( obj
) 内部会生成obj对应的递归锁,然后进行加锁、解锁操作
4 ) 性能比较差
, 苹果官方不推荐使用
, 所以代码提示也没有
9.iOS线程同步方案性能比较
10.自旋锁、互斥锁比较
a
) 什么情况使用自旋锁比较划算?
1 ) 预计线程等待锁的时间很短
2 ) 加锁的代码(临界区)经常被调用,但竞争情况很少发生
3 ) CPU资源不紧张
4 ) 多核处理器b
) 什么情况使用互斥锁比较划算?
1 ) 预计线程等待锁的时间较长
2 ) 临界区有IO操作
3 ) 临界区代码复杂或者循环量大
4 ) 临界区竞争非常激烈
5 ) 单核处理器c
) 临界区
: lock与unlock之间的代码 d
) IO操作
: 文件操作
( 读和写的操作
)
a
) atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁b
) 可以参考源码objc4的objc
- accessors
. mmc
) 它并不能保证使用属性的过程是线程安全的
a
) 思考如何实现以下场景
1 ) 同一时间,只能有
1 个线程进行写的操作
2 ) 同一时间,允许有多个线程进行读的操作
3 ) 同一时间,不允许既有写的操作,又有读的操作b
) 上面的场景就是典型的“多读单写”,经常用于文件等数据的读写操作c
) iOS中的实现方案有
1 ) pthread_rwlock:读写锁
2 ) dispatch_barrier_async:异步栅栏调用
2.1 ) 这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的
2.2 ) 如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果
a
) GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍b
) 源码地址:http
: c
) 虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值
a
) lldb查看汇编指令级别代码时一行一行的运行
1 ) stepi ( si
) 2 ) nexti ( ni
) : 也是一行一行
, 但是遇到函数调用会一笔带过这个函数调用
3 ) c
: 直接跳到下一个断点b
) 定义变量的时候同时给他赋值结构体可以这么干,但是不能直接给结构体赋值c
) 线程的任务一旦执行完毕,生命周期就结束了,无法再使用
总结
以上是生活随笔 为你收集整理的iOS开发-10.多线程 的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔 网站内容还不错,欢迎将生活随笔 推荐给好友。