iOS多線程各種安全鎖介紹 - 線程同步


 

一、atomic介紹

      github對應Demo:https://github.com/Master-fd/LockDemo

 

     在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];
    });
}

github對應Demo:https://github.com/Master-fd/LockDemo

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM