多线程编程里,会出现共同访问数据造成不安全的问题,网易的一个面试题很经典的诠释了线程安全。
@property (nonatomic, strong) NSString *target;
dispatch_queue_t queue = dispatch_queue_create("safe", DISPATCH_QUEUE_CONCURRENT);
for(int i =0; i <10000; i++) {
dispatch_async(queue, ^{ self.target= [NSStringstringWithFormat:@"hello%d",i]; });
}
上面这样写,会出现EXC_BAD_ACCESS崩溃现象,意思就是向一个已经释放的对象发送消息。出现崩溃的原因是过度释放,可能会出现多个队列同时释放.
- (void)setTarget:(NSString*)target {
[target retain];
[_target release];
_target = target;
}
针对这个问题的解决方案是,修饰符strong可以改为weak,nonatomic改为atomic加锁,使用串行队列都行。
不能确定代码的运行数据和结果,所以才导致崩溃,也就是线程不安全的,加锁是一种很好的解决方案,一般的锁有NSLock、@synchronized和dispatch_semaphore等,
@synchronized:(互斥锁),和NSLock相似,防止不同的线程同时获取相同的锁,@synchronized 的作用是创建一个互斥锁,保证此时没有其它线程对self对象进行修改。这个是objective-c的一个锁定令牌,防止self对象在同一时间内被其它线程访问,起到线程的保护作用。互斥锁的优点,能有效防止因为多线程抢夺资源造成的数据安全问题,缺点是需要消耗大量的CPU资源;@synchronized属于预防,当小括号里面的标志相同时才满足互斥,会造成线程阻塞,有点事不需要我们创建锁;
信号量dispatch_semaphore:终点在于这个函数,dispatch_semaphore_wait首先会先将信号量减一,并判断是否大于等于0,如果是,则返回0,并继续执行后续代码,否则,使线程进入睡眠状态,让出cpu时间。直到信号量大于0或者超时,则线程会被重新唤醒执行后续操作。这个函数会使传入的信号量dsema的值减1;这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,不能直接传入整形或float型数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句;
自旋锁:死循环,当A线程得到所以后,B线程想要获取锁就要等待A线程释放锁,期间一直处于等待状态;