这是我参与「第四届青训营 」笔记创作活动的第10天
原子操作、临界区、线程同步
单个机器指令执行的操作称为原子操作。
高级程序语言写出的一条语句,编译出来的往往是多条机器指令,此时称这条语句对应的操作不是原子的。如果多个线程对一个共享数据同时进行读写,会导致意想不到的后果。
临界区指的是不能被并发执行的一段代码(共享数据、代码块)。
线程同步指的是在一个线程访问临界区的时候,其他线程不能对临界区进行访问,即对临界区的访问变成原子化了。
锁
互斥量(Mutex) 是最简单的一种锁,在锁已经被占用的时候试图获取锁时,线程会等待,直到锁重新可用。由哪个线程获取就由哪个线程释放,常用于临界区的互斥访问。
信号量(Semaphore) 允许多个线程并发访问资源,可以实现线程同步或者控制并发访问的数量。
线程访问资源首先获取信号量:
- 信号量的值 -1
- 如果信号量的值小于 0,则线程进入等待状态,否则继续执行
访问完资源后线程释放信号量:
- 将信号量的值 +1
- 如果信号量的值大于 0,唤醒等待中的线程
死锁
死锁是指多个线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,它们都将无法推进下去,此时称系统处于死锁状态或系统产生了死锁
死锁产生的四个必要条件:
- 互斥条件:资源访问是互斥的
- 占有且等待条件:线程持有了资源不释放,同时请求其他资源
- 不可抢占条件:其它线程不能强制夺取资源,只能由占有资源的线程主动释放
- 循环等待条件:线程等待形成了环路
破坏产生死锁的四个必要条件之一可以预防死锁的发生
atomic、nonatomic
Objective-C 中定义一个类的属性时,可以指定该属性的原子性(atomic、nonatomic),默认是 atomic 的
atomic
- 对属性 getter、setter 调用是线程安全的
- 需要耗费资源为属性加锁
nonatomic
- 访问不是线程安全的
- 访问效率比 atomic 更高
dispatch_barrier_async
考虑多线程进行数据读写的场景,多个写操作不能并发执行,写操作和读操作不能并发执行,但是多个读操作是可以并发执行的,此时可以使用 dispatch_barrier_async 同时满足提高读操作效率和保证线程安全的要求。dispatch_barrier_async 函数会等队列中的全部任务执行结束后,再将指定的任务 X 追加到队列,之后提交的任务也需要等待 X 执行结束,仿佛 dispatch_barrier_async给队列添加了一道“栅栏”
Dispatch Semaphore
Dispatch Semaphore 是 GCD 提供的信号量接口,Dispatch Semaphore 可以实现成二元信号量或者多元信号量,达到线程同步、控制并发处理数量的效果