一、atomic介紹
在iOS中,@PRoperty 新增屬性時,可以增加atomic選項,atomic會給對應對setter方法加鎖,相當于
- (void)setTestStr:(NSString *)testStr{ @synchronizad(lock){ if (testStr != _testStr) { [_testStr release]; _testStr = [testStr retain]; } } }
那么就有問題了,為什么atomic又不是線程安全的呢??而且還會代理性能問題,比起nonatomic性能可能要大減20倍,如果頻繁的調用,可能更多。
1、當線程A,給TestStr設置值得時候,會調用對應的setter方法,也就是加鎖了,此時B線程也要對TestStr進行設置新值,因為A線程已經鎖住了,所以B只能等待,這個時候是線程安全的。
2、當線程A,給TestStr設置值得時候,此時B線程在讀TestStr的值,因為setter和getter方法是沒有聯系的,這時,A在執行到加鎖,只是還沒有設置值,然而B線程已經讀取走了,本來是想讀取A設置之后的值,卻讀取了設置之前的值,也就線程不安全了。
3、當線程A,給TestStr設置值得時候,C線程在A之前release了TestStr,此時就會導致崩潰,也就是線程不安全了。
總的來說,atomic只是保證了setter方法的安全,沒有保證對應成員變量的多線程安全,所以不是真正的線程安全。
二、線程安全的辦法
2.1、synchronizad 給需要加鎖的代碼進行加鎖。
- (IBAction)synchronizad:(id)sender { FDLog(@"synchronizad 測試"); static NSObject *lock = nil; if (!lock) { lock = [[NSString alloc] init]; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ FDLog(@"線程A,準備好"); @synchronized(lock){ FDLog(@"線程A lock, 請等待"); [NSThread sleepForTimeInterval:3]; FDLog(@"線程A 執行完畢"); } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ FDLog(@"線程B,準備好"); @synchronized(lock){ FDLog(@"線程B lock, 請等待"); [NSThread sleepForTimeInterval:1]; FDLog(@"線程B 執行完畢"); } });}
上面的AB線程都使用了同一把鎖,對相應代碼進行加鎖,所以鎖內的代碼是線程安全的。
2.2、NSLook 對多線程需要安全的代碼加鎖
- (IBAction)NSLook:(id)sender { static NSLock *lock = nil; if (!lock) { lock = [[NSLock alloc] init]; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ FDLog(@"線程A,準備好"); [lock lock]; FDLog(@"線程A lock, 請等待"); [NSThread sleepForTimeInterval:3]; FDLog(@"線程A 執行完畢"); [lock unlock]; }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ FDLog(@"線程B,準備好"); [lock lock]; FDLog(@"線程B lock, 請等待"); [NSThread sleepForTimeInterval:1]; FDLog(@"線程B 執行完畢"); [lock unlock]; }); }上面的AB線程都使用了同一把鎖,對相應代碼進行加鎖,所以鎖內的代碼是線程安全的。
2.3、NSCondition 條件鎖,只有達到條件之后,才會執行鎖操作,否則不會對數據進行加鎖
- (IBAction)NSCondition:(id)sender {#define kCondition_A 1#define kCondition_B 2 __block NSUInteger condition = kCondition_B; static NSConditionLock *conditionLock = nil; if (!conditionLock) { conditionLock = [[NSConditionLock alloc] initWithCondition:condition]; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ FDLog(@"線程A,準備好,檢測是否可以加鎖"); BOOL canLock = [conditionLock tryLockWhenCondition:kCondition_A]; if (canLock) { FDLog(@"線程A lock, 請等待"); [NSThread sleepForTimeInterval:1]; FDLog(@"線程A 執行完畢"); [conditionLock unlock]; }else{ FDLog(@"線程A 條件不滿足,未加lock"); } }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ FDLog(@"線程B,準備好,檢測是否可以加鎖"); BOOL canLock = [conditionLock tryLockWhenCondition:kCondition_B]; if (canLock) { FDLog(@"線程B lock, 請等待"); [NSThread sleepForTimeInterval:1]; FDLog(@"線程B 執行完畢"); [conditionLock unlock]; }else{ FDLog(@"線程B 未加lock"); } });}
2.4、NSRecursiveLock 遞歸鎖,同一個線程可以多次加鎖,但是不會引起死鎖,如果是NSLock,則會導致崩潰
- (void)reverseDebug:(NSUInteger )num lock:(NSRecursiveLock *)lock{ [lock lock]; if (num<=0) { FDLog(@"結束"); return; } FDLog(@"加了遞歸鎖, num = %ld", num); [NSThread sleepForTimeInterval:0.5]; [self reverseDebug:num-1 lock:lock]; [lock unlock];}- (IBAction)NSRecursiveLock:(id)sender { static NSRecursiveLock *lock = nil; if (!lock) { lock = [[NSRecursiveLock alloc] init]; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self reverseDebug:5 lock:lock]; });}
新聞熱點
疑難解答