您的位置:首页 > 移动开发 > IOS开发

iOS多线程资源共享的控制方法

2017-06-15 14:12 246 查看
1.  关于多线程的同步问题

使用多线程有许多我们众所周知的优点,比如响应度高、充分发挥多核处理器的性能等。但是每个线程的创建也都是有一定的成本。线程同步的官方解释 : 协同步调,让多个线程按照预定的先后顺序执行。 然而并不是所有的多线程要进行正确的工作都需要同步机制来管理。一般多线程执行,线程之间可能下面多种关系:

(1) 相对独立
比如一个负责图片下载的线程跟一个负责音频解码的线程基本上是相互独立的,虽然多线程存在,但是他们没有共享资源,不需要进行线程同步的管理。

(2)共同协作
比如多个线程同时从网络下载不同的图片,并更新数据库。只要保证更新数据库的过程是原子的就行。让多个线程的临界区不要同时执行。

(3)相互依赖
比如生产者与消费者模式,生产者要不要继续生产取决于缓冲区数据是否满了。消费者能不能继续消费取决于缓冲区是否有数据。这种除了要保证对缓冲区的原子操作之外,还要调整线程执行的顺序。

保证线程运行的成本:(内存+时间+调度+同步)
1. 线程管理块
2. 为线程分配的栈
3. 线程创建时间
4. OS对多线程进行唤醒与阻塞
5. 线程协作同步管理

所以说资源共享的控制逻辑,是多线程同步真正需要考虑的事情,这也是此文章题目为何叫《多线程资源共享的控制逻辑》,而不是简单的说多线程同步机制的原因。

2 从原子操作讲起
原子操作的业务抽象:
在上面线程的共同协作中我们提到了原子操作。在实际的线程协作或者相互依赖过程中,我们往往要对同一资源进行访问,这些资源包括,文件,外部设备,变量等。对每个共享变量 s-obj,我们通常的访问过程是 read s-obj , calculate s-obj, write s-obj.我们把这一系列操作抽象叫做RCW吧。对于不同的业务所必须限定的临界区并不一定都是RCW模式,具体需要原子操作的部分,要根据不同的业务场景来限定。
计算机对于原子操作的支持:
假设我们在单核的情况有两个线程,第一个线程在RCW的过程中发生系统中断进而进行sleep.第二个线程进来的时候进行了完整的RCW操作,那么当第一个线程重新唤醒的时候,第一个线程的计算往往就发生了错误。而在现在多核心的cpu上,其实发生的不仅仅是并发,多核心跑多线程其实是并行。在并发情况下我们要控制的是同一个时间在所有的cpu核心上必须进行完整的RCW操作。这个完整的RCW操作就是我们所说的原子操作。在单处理器上只要保证一个rcw的操作过程中不要出现系统中断就可以,如果是在多核系统上,仅仅保证rcw的操作不要被中断是不可行的,因为一直有可能出现rcw的并行执行。所以在多核操作系统中,原子操作是要并行安全的。设计原子操作对于软件和硬件设计都是一个非常大的挑战。原子操作是其他线程同步控制方法的核心基础。操作系统内核提供了一些对整形数据的原子操作指令。在<libkern/OSAtomic.h>这个库中。

3. 使用原子指令自己实现lock和semaphore
  #demo演示

4.  iOS多线程的实现方式
NSThread,
GCD
Operation Queue
NSObject的performInBg方法

POXIS Thread 是上述多线程实现方法的核心,具体使用如下:



5.  iOS多线程的同步控制方法
5.1 硬件原子操作指令
参考第二部分原子操作指令。

5.2 Memory Barriers & Volatile  Variables
为了获得最理想的性能,编译器会对程序生成的汇编指令进行重新排序,在重新排序的过程中,对于读取在寄存器中的一些内容,很多时候基于内置的优化算法,并不会每次读取某个值的时候都直接从内存读取,而是会直接从寄存器读取。这样对于经常变化的某个区域内存,可能会由于编译器的优化算法而导致读取的数值错误。
OSMemoryBarrier:
memory barriers是为了保证汇编指令按照应该有的顺序,顺序执行。
volatile关键字:
 声明的变量,每次获取其值的时候,都要重新从内存中读取

5.3 pthread_mutex_t (poxis 互斥锁) 
互斥锁的类型: 



poxis互斥锁能够保证临界区的原子执行,但是没办法很好的控制执行的顺序。为了更好的控制线程执行的顺序,poxis mutex 有时候需要跟 poxis condition协同使用。



5.4 pthread_cond_t  (poxis condition)
条件变量的使用主要在于能够控制某线程在获得锁的时候,虽然线程获得了锁,但是你想让它先其释放锁的时候,有一个锁权利转让的机制。
比如生产者,消费者。有可能在缓冲区是空的时候,想要让其让度锁。让当前线程释放锁,并阻塞。让其他线程早点执行。条件变量用来阻塞一个线程,直到某个条件满足的时候才唤醒。
pthread_cond_wait(&condition, &mutex); //等待mutex和condition都被释放后才唤醒执行,检测condition,并释放mutex锁
pthread_cond_signal(&condition); //发送condition满足的信号

5.5  NSLock
NSLock 只是在内部封装了一个 pthread_mutex_t,属性PTHREAD_MUTEX_ERRORCHECK。
除了lock和unlock之外还提供了,trylock和lockbeforedate方法。
trylock马上返回,业务上可以根据当前需求做处理。
lbd在指定时间之前一直尝试获取锁,在这之前获取了返回yes,不然到指定时间返回NO.
NSLock使用POSIX线程实现锁功能,对于一个锁进行unlock的时候,必须保证方法调用所在的线程跟执行lock操作的线程一样。在不同的线程进行解锁,会产生不可预测的结果。
不能对lock执行连续的锁操作,也就是递归锁,要做递归锁的话,用NSRecursiveLock.

5.6 NSRecursiveLock
封装了 PTHREAD_MUTEX_RECURSIVE 类型的pthread_mutex_t.支持递归加锁。

5.7 OSSpinLock
简单的自旋锁,初始化后,直接通过lock和unlock保护临界区。只不过其在没有获取到锁的时候是进行自旋,而不是阻塞。但是,据说OSSpinLock在某些情况下会造成死锁。如果等待时机非常短,尤其在多核处理架构下,用自旋锁会获得很好的性能。

5.8 @synchronized 指令
用一个递归互斥锁实现两个函数:
objc_sync_enter
objc_sync_exit
参考链接:http://rykap.com/objective-c/2015/05/09/synchronized/

5.9 NSCondition
类似生产者与消费者模式的业务模式,封装了pthread_mutex_t和pthread_cond_t。

5.10 NSConditionLock
lock 试着获取锁
lockwhencondition:cnd, 只有当满足条件的时候才试着获取锁,不然就阻塞
unlockwithcondition:cnd 释放锁,并把内部条件改成cnd.
此condition跟poxis condition没有关系。

1. NSConditionLock 相关知识点:
 1.1  NSConditionLock 是锁,一旦一个线程获得锁,其他线程一定等待 1.2  [xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执行此行以下代码,如果已经有其他线程获得锁(可能是条件锁,或者无条件锁),则等待,直至其他线程解锁
 1.3  [xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直至它解锁。
 1.4  [xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件
 1.5  return = [xxx lockWhenCondition:A条件 beforeDate:A时间]; 表示如果被锁定(没获得锁),并超过该时间则不再阻塞线程。但是注意:返回的值是NO,它没有改变锁的状态,这个函数的目的在于可以实现两种状态下的处理
 1.6 所谓的condition就是整数,内部通过整数比较条件

5.11 dispatch_semaphore_t
dispatch_semaphore_create(1)
dispatch_semaphore_wait
dispatch_semaphore_signal
根据初始化的信号量的大小,来觉得能够进入临界区的数量。

5.12 读写锁,分布式锁...

总结:
1 多线程同步的核心问题是共享资源的控制。
2 共享资源的控制基础是计算机本身实现的硬件原子操作指令。
3 iOS各种锁的实现主要还是基于poxis的mutex
4 如果只是保护临界区原子执行,那么poxis mutex和普通的lock就够了。
5 如果为了控制进入临界区的线程数量,可以考虑semaphore.
6如果线程有协作和相互依赖,为了高效执行,可以引入条件变量。
7 如果要考虑原子操作又要严格控制线程协作的顺序,可以使用NSConditionLock.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios 多线程 同步