Bison眼中的iOS开发多线程是这样的(二)
"多线程很容易突然出现“错误情况”,这是由于系统的线程调度具有一定的随机性造成的。不过,即使程序偶然出现问题,那也是由于编程不当所引起的。当使用多个线程来访问同一个数据时,很容易“偶然”出现线程安全问题。"
前面在《Bison眼中的iOS开发多线程是这样的(一)》一文中讲到多线程的优先级,接下来我们讲讲线程同步与线程通信
多线程很容易突然出现“错误情况”,这是由于系统的线程调度具有一定的随机性造成的。不过,即使程序偶然出现问题,
那也是由于编程不当所引起的。当使用多个线程来访问同一个数据时,很容易“偶然”出现线程安全问题。
关于线程安全问题,有OC的多线程支持引入了同步,使用同步的通用方法就是@synchronized
修饰代码块,
被@synchronized
修饰的代码块可简称为同步代码块。同步代码块的语法格式如下:
@synchronized(obj){
...
//此处的代码即为同步代码块
}
上面语法格式中@synchronized
后面括号里的obj
就是同步监视器。上面代码的含义是:
线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。值得注意的是,人和时刻只能有
一个线程可以获得对同步监视器的锁定,当同步代码块执行完后,该线程会释放对同步监视器的锁定
虽然OC允许使用任何对象作为同步监视器,但想一下同步监视器的目的:阻止俩个线程对同一共享资
源进行并发访问,因此通常推荐使用可能被并发访问的共享资源充当同步监视器
线程安全的类具有如下特征:
该类的对象可以被多个线程安全的访问。
每个线程调用该对象的任意方法之后都将得到正确结果。
每个线程调用该对象的任意方法之后,该对象依然保持合理状态。
Foundation
框架中很多类都是有可变和不可变俩种版本,其中不可变类总是线程安全的,因为它的
对象状态不可改变。而可变类的对象需要额外的方法来保证其线程安全。
将多个线程并发修改共享资源的临界区使用@synchronized
修饰,这样即可保证任意时刻,最多只
能有一个线程进入临界区修改共享数据,从而就可以实现线程安全的类。
可变类的线程安全是以降低程序的运行效率作为代价的。为了减少线程安全所带来的负面影响,程序可
以采用如下策略:
不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源(共享资源)对方法进行同步。
如果可变类有俩种运行环境:单线程环境和多线程环境,则应该为该可变类提供俩种版本,线程不安全版本和安全版本。在单线程环境中使用线程不安全版本已保证性能,在多线程环境中使用线程安全版本。
任何线程在进入同步代码块之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器的锁定呢?程序无法显示释放对同步监视器的锁定,线程会再如下几种情况下释放对同步监视器的锁定。
当线程的同步代码块执行结束,当前线程即将释放同步监视器。
当线程的同步代码块中遇到 goto、 return终止了该代码块、该方法的继续执行时,当前线程将会释放同步监视器。
当线程的同步代码块中出现了错误,导致该代码块异常结束时,将会释放同步监视器。典型的例子有:当程序调用
NSThread
的sleepXxx
方法暂停线程时,线程不会释放同步监视器。
Foundation
还提供了NSLock
,通过显示定义同步锁对象来实现同步,在这种机制下,同步锁使用NSLock
对象充当。
NSLock
是控制多个线程对共享资源进行访问的工具。通常锁提供了对共享资源的独占访问,每次只能有一个线程对NSLock
对象加锁,线程开始访问共享资源之前应县获得NSLock
对象。
在实现线程安全的控制中,使用该NSLock
对象可以显式的加锁、释放锁。下面我们来举个简单的🌰
NSLock* lock;
- (id)init
{
self = [super init];
if (self) {
lock = [[NSLock alloc] init];
}
return self;
}
//定义需要保证线程安全的方法
- (void)Safety{
[lock lock];
//需要保证线程安全的方法
[lock unlock];
}
当线程在系统内运行,线程的调度具有一定的透明度,程序通常无法准确控制线程的轮换执行,但我们可以通过一些机制来保证线程协调运行,也就是处理线程之间的通信。
对此Foundation
提供了NSCondition
类来处理多线程之间的通信,NSCondition
实现了NSLock
协议,因此也可以调用lock
、unlock
来实现线程同步。除此之外,NSCondition
可以让那些已经锁定NSCondition
对象却无法继续执行的线程释放NSCondition
对象,NSCondition
对象也可以唤醒其他处于等待状态的线程。
NSCondition
提供了如下几个方法
//改方法导致当前线程一直等待,直到其他线程调用该NSCondition的signal方法或者broadcast方法
- (void)wait;
//用于控制等待到指定时间点,如果到了该时间点,该线程将会被自动唤醒
- (BOOL)waitUntilDate:(NSDate *)limit;
//唤醒等待的单个线程,如果有多个,则随机唤醒一个。只有当前线程放弃对该NSCondition对象的锁定后(wait方法),才可执行
- (void)signal;
//唤醒等待的所有线程
- (void)broadcast;
今天暂时写到这!
预告:(三)主讲GCD多线程!
如对你有帮助,请不要吝惜你的star和喜欢哦!
技术交流群:534926022(免费) 511040024(0.8/人付费)