属性关键字 atomic 和 nonatomic
仅仅能够保证setter、getter方法获取数据的完整性,但不能保证多线程安全
// nonatomic
-(void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
//非原子性的getter可能不完整就出现在这里
_name = [name retain];
}
}
// atomic
-(void)setName:(NSString *)name
{
@synchronized(self) {
if (_name != name) {
[_name release];
_name = [name retain];
}
}
}
多线程数组安全问题:
-
问题一:多线程同时对同一个数据进行修改,那么无法保证最后修改的数据是我们期望的,有可能是A线程的修改结果,也可能是B线程的修改结果
/* 并发队列 + 栅栏 (无法通过同步执行) dispatch_barrier 栅栏 dispatch_barrier_async 不阻塞主线程 dispatch_barrier_sync 阻塞主线程 */ dispatch_async(queue2, ^{ sleep(2); self.person.name = @"Wang"; NSLog(@"Wang"); }); dispatch_async(queue2, ^{ sleep(1); self.person.name = @"King"; NSLog(@"King"); }); dispatch_barrier_async(queue, ^{ sleep(2); self.person.age = @"1"; NSLog(@"1"); }); dispatch_barrier_async(queue, ^{ sleep(1); self.person.age = @"2"; NSLog(@"2"); }); NSLog(@"先走");
-
问题二:在子线程中读取数据的同时,在另一个子线程中删除数据,读取到的可能就是空
异步async+并发DISPATCH_QUEUE_CONCURRENT = 不阻塞线程,但是不能防止读到空数据
异步async+串行DISPATCH_QUEUE_SERIAL = 不阻塞线程,但是不能防止读到空数据
同步sync+并发DISPATCH_QUEUE_CONCURRENT = 阻塞线程,能防止读到空数据
异步sync+串行DISPATCH_QUEUE_SERIAL = 阻塞线程,能防止读到空数据
总结: 异步无法按序执行,同步会阻塞线程,可以 加锁 来解决问题或者参照上面用 栅栏
iOS 开发中用到的锁
自旋锁、互斥锁、读写锁、信号量、条件锁
-
自旋锁,用于多线程同步数据,会造成线程阻碍,直至显示的释放锁。可以避免进程上下文的调度开支,适合只会阻塞很短时间的场合。
- OSSpinLock
// OSSpinLock iOS 10 已废弃
#import <libkern/OSAtomic.h>
// OS_SPINLOCK_INIT: 默认值为 0,在 locked 状态时就会大于 0,unlocked 状态下为 0
// OSSpinLockLock(&oslock):上锁,参数为 OSSpinLock 地址
// OSSpinLockUnlock(&oslock):解锁,参数为 OSSpinLock 地址
// OSSpinLockTry(&oslock):尝试加锁,可以加锁则 立即加锁 并返回 YES,反之返回 NO
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
(子线程中) {
OSSpinLockLock(&oslock);
// doSomething
OSSpinLockUnlock(&oslock);
}
-
互斥锁,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。(临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。)
queue = dispatch_queue_create("CONCURRENT122", DISPATCH_QUEUE_CONCURRENT);
queue2 = dispatch_queue_create("CONCURRENT233", DISPATCH_QUEUE_CONCURRENT);
queue3 = dispatch_queue_create("CONCURRENddd3", DISPATCH_QUEUE_CONCURRENT);
_lock = [[NSLock alloc]init];
_lock.name = @"1111";
dispatch_async(queue, ^{
NSLog(@"in1");
[self->_lock lock];
sleep(3);
NSLog(@"go1");
[self->_lock unlock];
});
sleep(1);
dispatch_async(queue2, ^{
NSLog(@"in2");
[self->_lock lock];
NSLog(@"go2");
[self->_lock unlock];
});
- pthread_mutex:YYKit的YYMemoryCach中可以看到
pthread_mutex_t _lock;
pthread_mutex_init(&_lock, NULL);
pthread_mutex_lock(&_lock);
pthread_mutex_unlock(&_lock);
pthread_mutex_trylock(&_lock)
pthread_mutex_unlock(&_lock);
- @synchronized:
- (BOOL)isNetworkActivityOccurring {
@synchronized(self) {
return self.activityCount > 0;
}
}
- 读写锁,又称共享-互斥锁,
//加读锁
pthread_rwlock_rdlock(&rwlock);
//解锁
pthread_rwlock_unlock(&rwlock);
//加写锁
pthread_rwlock_wrlock(&rwlock);
//解锁
pthread_rwlock_unlock(&rwlock);
-
递归锁:同一个线程可以加锁N次而不会引发死锁。
- NSRecursiveLock
- (NSInteger)try:(NSInteger)count {
// 递归函数内,需要用到递归锁,否则死锁
[lock lock];
NSLog(@"输入:%ld",(long)count);
if (count > 1) {
count -= 1;
return [self try:count];
}else {
return count;
}
[lock unlock];
}
- pthread_mutex(recursive)
pthread_mutex_t lock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&lock, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&lock);
pthread_mutex_unlock(&lock);
-
条件锁
-
NSCondition
遵循NSLocking协议,使用的时候同样是lock,unlock加解锁,wait是傻等,waitUntilDate:方法是等一会,都会阻塞掉线程,signal是唤起一个在等待的线程,broadcast是广播全部唤起。
-
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
- NSConditionLock
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
网友评论