線程鎖

(atomic是隻對set加鎖,對get不加鎖,因此在獲取的時候會有偏差)多線程

·@synchronized異步

synchronized的使用形式是async

@synchronized (object) {
    //須要保護的代碼塊
}

設計的一個類測試

@interface SynchronizObj : NSObject

- (void)synchronizMethod;
- (void)normalMethod;

@end

@implementation SynchronizObj

- (void)synchronizMethod
{
    @synchronized (self) {
        
        NSLog(@"synchronizMethod");
        sleep(5);
        
    }
}

- (void)testLock
{
    static NSObject *lock = nil;
    
    if (!lock) {
        lock = [[NSString alloc] init];
    }
        
    @synchronized(lock){
        NSLog(@"開始枷鎖");
        sleep(5);
    }
}

- (void)normalMethod
{
    NSLog(@"normalMethod");
}

@end

(1)直接調用這個類atom

SynchronizObj *obj = [[SynchronizObj alloc] init];
    
[obj synchronizMethod];
[obj normalMethod];

獲得的結果是spa

19:31:09.855 LockDemo[1120:135454] synchronizMethod
19:31:14.857 LockDemo[1120:135454] normalMethod

相差了5秒執行.操作系統

(2)咱們把這個放到異步執行裏面看一下如何.net

dispatch_async(dispatch_get_main_queue(), ^{
    [obj testLock];
});

dispatch_async(dispatch_get_main_queue(), ^{
    sleep(1);//讓synchronizMethod先執行
    [obj testLock];
});

結果
22:12:21.745 LockDemo[1144:141456] 開始枷鎖
22:12:27.889 LockDemo[1144:141446] 開始枷鎖

放到異步裏,只相差6秒執行,能夠看到,testLock中@synchronized保護的代碼塊,在未返回的狀況下,其餘線程是沒法訪問。線程

(3)@synchronized須要一個參數,若是傳遞的是不一樣的參數呢?咱們把測試的類的代碼修改一下設計

- (void)synchronizMethod:(id)obj;
- (void)normalMethod:(id)obj;

- (void)synchronizMethod:(id)obj
{
    @synchronized (self) {
        
        NSLog(@"synchronizMethod:%@", obj);
        sleep(5);
        
    }
}

- (void)normalMethod:(id)obj
{
    @synchronized (self) {
        NSLog(@"normalMethod");
    }
}

調用代碼和結果

SynchronizObj *obj = [[SynchronizObj alloc] init];
SynchronizObj *obj1 = [[SynchronizObj alloc] init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [obj synchronizMethod:obj];
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//讓obj synchronizMethod先執行
    [obj normalMethod:obj];
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);//讓obj synchronizMethod先執行
    [obj1 synchronizMethod:obj];
});

結果

20:05:46.491 LockDemo[1199:156733] synchronizMethod:<SynchronizObj: 0x7fd632f0b000>
20:05:47.492 LockDemo[1199:156740] synchronizMethod:<SynchronizObj: 0x7fd632f0b000>
20:05:51.497 LockDemo[1199:156744] normalMethod

能夠看到,多線程中,@synchronized獲取的是不一樣的參數,是不會阻塞的,但若是是同一個參數,則只有一個會得到鎖,直到鎖保護的內容執行完畢。

·NSLock

NSLock等是實現了NSLocking協議,因此具有了lock和unlock這一對上鎖解鎖的方法,這一對方法須要在同一線程中,不然會致使報錯(先調用了unlock,沒有或者後調用了lock)或者沒法持有鎖(上一個lock未調用unlock)。

通常是這樣調用

NSLock* lock = [[NSLock alloc] init];
    
dispatch_async(dispatch_get_main_queue(), ^{
    [lock lock];
    NSLog(@"任務1");
    sleep(1);
    [lock unlock];
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);// 讓任務1先執行
    [lock lock];
    NSLog(@"任務2");
    [lock unlock];
});

結果
20:23:43.869 LockDemo[1263:169163] 任務1
20:23:44.870 LockDemo[1263:169200] 任務2

·NSConditionLock

NSConditionLock顧名思義,叫作條件鎖,根據某種條件進行上鎖解鎖,但這個條件,必須是個NSInteger變量。

NSConditionLock有這麼幾個特別的地方:

- (instancetype)initWithCondition:(NSInteger)condition;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;

NSConditionLock有個condition屬性,只讀的,使用上面兩個方法進行上鎖和解鎖的時候,lockWhenCondition時,會將參數condition和自身的condition屬性進行比較,若是相同,才能進行上鎖操做,不然會處於等待;unlockWithCondition時,會將參數condition自動賦值給自身的condition屬性,用於以後的對比。一樣,調用initWithCondition初始化,也會將condition賦值。

文檔中用生產和消費舉例,下面是個人測試代碼和結果

NSConditionLock* lock = [[NSConditionLock alloc] init];
NSMutableArray* products = [NSMutableArray array];

#define hasData YES
#define noData NO

dispatch_async(dispatch_get_main_queue(), ^{
    
    for (int i = 0; i < 3; i ++) {
        
        [lock lockWhenCondition:noData];
        
        [products addObject:@"product"];
        NSLog(@"生產產品%d condition:%ld", i, lock.condition);
        
        [lock unlockWithCondition:hasData];
        
    }
    
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);// 讓任務1先執行
    
    for (int i = 0; i < 3; i ++) {
        
        [lock lockWhenCondition:hasData];
        
        [products removeAllObjects];
        NSLog(@"消費產品%d condition:%ld", i, lock.condition);
        
        [lock unlockWithCondition:noData];
        
    }
    
});


結果
20:45:59.579 LockDemo[1297:184273] 生產產品0 condition:0
20:46:00.580 LockDemo[1297:184322] 消費產品0 condition:1
20:46:00.580 LockDemo[1297:184273] 生產產品1 condition:0
20:46:00.581 LockDemo[1297:184322] 消費產品1 condition:1
20:46:00.581 LockDemo[1297:184273] 生產產品2 condition:0
20:46:00.581 LockDemo[1297:184322] 消費產品2 condition:1

·NSRecursiveLock

NSRecursiveLock,遞歸鎖,容許在同一線程中屢次持有鎖,但每次持有必須配對解鎖。

@property (nonatomic, strong) NSRecursiveLock* lock;

- (void)testMethod {
    
    _lock = [[NSRecursiveLock alloc] init];
    [self testRecursiveLock:5];
    
}

- (void)testRecursiveLock:(NSInteger)value
{
    
    [_lock lock];
    
    if (value > 0) {
        
        NSLog(@"第%ld次持有鎖", 6 - value);
        
        -- value;
        [self testRecursiveLock:value];
        
    }
    
    [_lock unlock];
    
}

結果
21:06:27.322 LockDemo[1327:190602] 第1次持有鎖
21:06:27.323 LockDemo[1327:190602] 第2次持有鎖
21:06:27.323 LockDemo[1327:190602] 第3次持有鎖
21:06:27.323 LockDemo[1327:190602] 第4次持有鎖
21:06:27.323 LockDemo[1327:190602] 第5次持有鎖

不一樣線程持有鎖,按照基本狀況,即先持有鎖的線程先執行,直到最終釋放鎖。好比對上面的代碼作修改,在遞歸期間,有另外一線程請求鎖

- (void)testMethod {
    
    _lock = [[NSRecursiveLock alloc] init];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        sleep(1);// 讓遞歸程序先執行
        [_lock lock];
        NSLog(@"異步加鎖");
        sleep(5);
        NSLog(@"異步解鎖");
        [_lock unlock];
        
    });
    
    [self testRecursiveLock:5];
    
}

- (void)testRecursiveLock:(NSInteger)value
{
    
    [_lock lock];
    
    if (value > 0) {
        
        NSLog(@"第%ld次持有鎖", 6 - value);
        
        sleep(1);
        -- value;
        [self testRecursiveLock:value];
        
    }
    
    [_lock unlock];
    
}

結果
21:12:03.876 LockDemo[1357:194699] 第1次持有鎖
21:12:04.877 LockDemo[1357:194699] 第2次持有鎖
21:12:05.878 LockDemo[1357:194699] 第3次持有鎖
21:12:06.879 LockDemo[1357:194699] 第4次持有鎖
21:12:07.879 LockDemo[1357:194699] 第5次持有鎖
21:12:08.881 LockDemo[1357:194735] 異步加鎖
21:12:13.884 LockDemo[1357:194735] 異步解鎖

上面的測試代碼,先在主線程持有遞歸鎖,其餘線程以後請求鎖的時候,須要等待主線程的遞歸鎖把鎖保護內容執行完畢,即遞歸調用完成,而不是在某一次的unlock後就得到鎖資源。

·NSCondition

NSCondition看起來和NSConditionLock很像,但其實差異仍是挺大的。

文檔中推薦NSCondition的使用步驟是

  • lock the condition
  • while (!(boolean_predicate)) {
  •     wait on condition
  • }
  • do protected work
  • (optionally, signal or broadcast the condition again or change a predicate value)
  • unlock the condition

 NSCondition的使用和POSIX很相似,都須要加(推薦加)boolean_predicate來確保這個過程。由於操做系統的設計,容許NSCondition返回一個虛假的成功信號,在沒有觸發你的代碼狀況下,喚醒NSCondition鎖住的線程。

測試代碼以下

@property (atomic, assign) BOOL ready_to_go;


NSCondition* condition = [[NSCondition alloc] init];

_ready_to_go = NO;

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
    [condition lock];
    
    if (_ready_to_go == NO) {
        
        NSLog(@"等待1");
        
        [condition wait];
        
    }
    
    NSLog(@"執行1");
    
    [condition unlock];
    
});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
    sleep(3);
    [condition lock];
    
    _ready_to_go = YES;
    
    NSLog(@"執行2");
    
    [condition broadcast];//觸發全部等待condition的線程
    // 也可使用這個[condition signal];
    
    [condition unlock];
    
});

結果
21:48:04.434 LockDemo[1392:217171] 等待1
21:48:07.435 LockDemo[1392:217182] 執行2
21:48:07.435 LockDemo[1392:217171] 執行1

雖然任務1先執行,但因爲標識不符合,因此進入等待,任務2執行後,更新標識,才觸發到任務1的執行。

相關文章
相關標籤/搜索