iOS中常見的鎖

來源:互聯網
上載者:User

標籤:blog   .com   images   bsp   異常處理   解決問題   test   property   max   

多線程的安全隱患

一塊資源可能會被多個線程共用,也就是說多個線程可能會訪問同一塊資源。

比如多個線程同時操作同一個對象,同一個變數。

當多個線程訪問同一塊資源時,很容易引發資料錯亂和資料安全問題。

比如一個買票問題:

#import "ViewController.h"@interface ViewController ()@property (assign, nonatomic) NSInteger maxCount;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];

   _maxCount = 20; // 線程1 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; // 線程2 [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; }- (void)run{ while (1) { if (_maxCount > 0) { // 暫停一段時間 [NSThread sleepForTimeInterval:0.002]; _maxCount--; NSLog(@"賣了一張票 - %ld - %@",_maxCount,[NSThread currentThread]); }else{ NSLog(@"票賣完了"); break; } }}

 輸出結果:

可以看到,當多個線程同時訪問同一個資料的時候,很容易出現資料錯亂,資源爭奪的現象。

1. @synchronized(鎖對象) { // 需要鎖定的代碼  } 來解決

互斥鎖,使用的是線程同步的技術。加鎖的代碼需要盡量少,這個鎖 ?? 對象需要保持在多個線程中都是同一個對象。

優點:是不需要顯示的建立鎖對象,就可以實現鎖的機制。

缺點:會隱式的添加一個異常處理常式來保護代碼,該處理常式會在異常拋出的時候自動的釋放互斥鎖,會消耗系統資源。

- (void)run{    while (1) {        // 這裡使用self,或者一個全域對象也行,每個對象裡面都有一把鎖        @synchronized(self){            if (_maxCount > 0) {                // 暫停一段時間                [NSThread sleepForTimeInterval:0.002];                _maxCount--;                NSLog(@"賣了一張票 - %ld - %@",_maxCount,[NSThread currentThread]);            }else{                NSLog(@"票賣完了");                break;            }        }    }}

 輸出結果:

2. NSLock

在Cocoa架構中,NSLock實現了一個簡單的互斥鎖,所有鎖(包括NSLock)的介面,實際上都是通過NSLocking協議定義的,它定義了 lock(加鎖)和 unlock(解鎖)方法。

不能多次調用lock方法,會造成死結。

我們使用NSLock來解決上面的問題:

#import "ViewController.h"@interface ViewController ()@property (assign, nonatomic) NSInteger maxCount;@property (strong, nonatomic) NSLock *lock; // 資料所@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.        _maxCount = 20;        _lock = [[NSLock alloc] init];        // 線程1    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];        // 線程2    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];    }- (void)run{    while (1) {        // 加鎖        [_lock lock];        if (_maxCount > 0) {            // 暫停一段時間            [NSThread sleepForTimeInterval:0.002];            _maxCount--;            NSLog(@"賣了一張票 - %ld - %@",_maxCount,[NSThread currentThread]);        }else{            NSLog(@"票賣完了");            break;        }        // 釋放鎖        [_lock unlock];    }}

 輸出結果:

3. NSRecursiveLock 遞迴鎖

如果在迴圈中,使用鎖,很容易造成死結。如下代碼,在遞迴block中,多次的調用lock方法,鎖會被多次的lock,所以自己也被阻塞了。

_lock = [[NSLock alloc] init];        //線程1    dispatch_async(dispatch_queue_create(NULL, 0), ^{        static void(^TestMethod)(int);        TestMethod = ^(int value)        {            [_lock lock];            if (value > 0)            {                [NSThread sleepForTimeInterval:1];                NSLog(@"執行一次哦");                TestMethod(value--);            }            NSLog(@"是否執行到這裡");            [_lock unlock];        };                TestMethod(5);    });

 輸出內容:

2017-12-12 11:39:50.253155+0800 NSLock[1353:158620] 執行一次哦

此處將NSLock 換成 NSRecursiveLock 便可解決問題:

 NSRecursiveLock類定義的鎖可以在同一線程多次lock,而不會造成死結。遞迴鎖會跟蹤它會被多少次lock,每次成功的lock都必須平衡調用unlock操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其它線程獲得。

_lock = [[NSRecursiveLock alloc] init];        //線程1    dispatch_async(dispatch_queue_create(NULL, 0), ^{        static void(^TestMethod)(int);        TestMethod = ^(int value)        {            [_lock lock];            if (value > 0)            {                [NSThread sleepForTimeInterval:1];                NSLog(@"執行了一次哦");                TestMethod(value--);            }            NSLog(@"是否執行到這裡");            [_lock unlock];        };                TestMethod(5);    });    

 執行結果:

2017-12-12 11:49:43.378299+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:44.380543+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:45.382145+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:46.387148+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:47.388813+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:48.389408+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:49.392983+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:50.396521+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:51.399108+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:52.399976+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:53.404280+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:54.409044+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:55.412670+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:56.413754+0800 NSLock[1419:176157] 執行了一次哦2017-12-12 11:49:57.414257+0800 NSLock[1419:176157] 執行了一次哦
4. NSConditionLock 條件鎖

 NSConditionLock相比NSLock多了一個condition參數,我們可以理解為一個條件標示。

@property (readonly)NSInteger condition; //這屬性非常重要,外部傳入的condition與之相同才會擷取到lock對象,反之阻塞當前線程,直到condition相同- (void)lockWhenCondition:(NSInteger)condition; //condition與內部相同才會擷取鎖對象並立即返回,否則阻塞線程直到condition相同- (BOOL)tryLock;//嘗試擷取鎖對象,擷取成功需要配對unlock- (BOOL)tryLockWhenCondition:(NSInteger)condition; //同上- (void)unlockWithCondition:(NSInteger)condition; //解鎖,並且設定lock.condition = condition

NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0];        //線程1    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        if([cLock tryLockWhenCondition:0]){            NSLog(@"線程1");            [cLock unlockWithCondition:1];        }else{            NSLog(@"失敗");        }    });        //線程2    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [cLock lockWhenCondition:3];        NSLog(@"線程2");        [cLock unlockWithCondition:2];    });        //線程3    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        [cLock lockWhenCondition:1];        NSLog(@"線程3");        [cLock unlockWithCondition:3];    });

 輸出結果:

2017-12-12 13:32:24.060013+0800 條件鎖 - NSConditionLock[1783:250080] 線程12017-12-12 13:32:24.060461+0800 條件鎖 - NSConditionLock[1783:250078] 線程32017-12-12 13:32:24.060626+0800 條件鎖 - NSConditionLock[1783:250079] 線程2

  - 我們在初始化這個lock的時候,給定了它的初始條件為0;

  - 執行tryLockWhenCondition:時,我們傳入的條件標示也是0,所以線程1加鎖成功。

  - 執行unLockWithCondition:時,這時候會把condition將0改為1

  - 因為condition為1,所以先走線程3,然後線程3將condition修改為3,

  - 最後走了線程2

可以看出,NSConditionLock還可以實現任務之間的依賴。

5. NSCondition 

NSCondition實際上作為一個鎖和一個線程檢查器。鎖主要是為了檢測條件時保護資料來源,執行條件引發的任務;線程檢查器主要是根據條件決定是否繼續進行下去,即線程是否被阻塞。

NSCondition實現了NSLocking協議,當多個線程訪問同一段代碼,會以wait為分水嶺,一個線程等待另一個線程unlock後,才會走wait之後的代碼。

[condition lock];//一般用於多線程同時訪問、修改同一個資料來源,保證在同一時間內資料來源只被訪問、修改一次,其他線程的命令需要在lock 外等待,只到unlock ,才可訪問[condition unlock];//與lock 同時使用[condition wait];//讓當前線程處於等待狀態[condition signal];//CPU發訊號告訴線程不用在等待,可以繼續執行

 我們可以來看一個生產者消費者的問題,

消費者取得鎖,取產品,如果沒有產品,則wait等待,這時候會釋放鎖,知道有線程去喚醒它去消費產品。

生產者製造產品,首先也要取得鎖,然後生產,再發signal,這樣可以喚醒wait的消費者

#import "ViewController.h"@interface ViewController ()@property (strong, nonatomic) NSCondition *condition;@property (assign ,nonatomic) NSInteger goodNum;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];        _goodNum = 0;        _condition = [[NSCondition alloc] init];        [NSThread detachNewThreadSelector:@selector(buyGoods) toTarget:self withObject:nil];        [NSThread detachNewThreadSelector:@selector(shopGoods) toTarget:self withObject:nil];    }- (void)buyGoods{    [_condition lock];    while (_goodNum == 0){        NSLog(@"當前售賣個數為0,不能賣,等待");        [_condition wait];    }    _goodNum--;    NSLog(@"買了一個 - %ld",_goodNum);    [_condition unlock];}- (void)shopGoods{    [_condition lock];    _goodNum++;    NSLog(@"準備賣一個 - %ld",_goodNum);    [_condition signal];    NSLog(@"告訴買家可以買了");    [_condition unlock];}

 輸出結果:

2017-12-12 14:43:48.995787+0800 NSCondition[2410:357340] 當前售賣個數為0,不能賣,等待2017-12-12 14:43:48.996067+0800 NSCondition[2410:357341] 準備賣一個 - 12017-12-12 14:43:48.996578+0800 NSCondition[2410:357341] 告訴買家可以買了2017-12-12 14:43:48.997806+0800 NSCondition[2410:357340] 買了一個 - 0

 我們可以看出,當消費者想要買東西的時候,因為商品初始數量為0,所以只能等待生產者去生產商品,準備售賣,有商品之後,會signal,告訴消費者可以買了。

6. 使用C語言的pthread_mutex_t實現的鎖
#import "ViewController.h"#import <pthread.h>@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    __block pthread_mutex_t mutex;        pthread_mutex_init(&mutex, NULL);        //線程1    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        pthread_mutex_lock(&mutex);        NSLog(@"線程1開始執行");        for (NSInteger i = 1; i <= 10; i++) {            sleep(1);            NSLog(@"線程1 在執行 ---- %ld",i);        }        pthread_mutex_unlock(&mutex);    });    //線程2    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{        pthread_mutex_lock(&mutex);        NSLog(@"線程1執行完了,線程2開始執行");        pthread_mutex_unlock(&mutex);    });    }

 輸出結果:

2017-12-12 14:54:16.343540+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1開始執行2017-12-12 14:54:17.348019+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 12017-12-12 14:54:18.348439+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 22017-12-12 14:54:19.351159+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 32017-12-12 14:54:20.355283+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 42017-12-12 14:54:21.358290+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 52017-12-12 14:54:22.361572+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 62017-12-12 14:54:23.362013+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 72017-12-12 14:54:24.363130+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 82017-12-12 14:54:25.366042+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 92017-12-12 14:54:26.370250+0800 pthread_mutex - 互斥鎖[2518:371518] 線程1 在執行 ---- 102017-12-12 14:54:26.370496+0800 pthread_mutex - 互斥鎖[2518:371519] 線程1執行完了,線程2開始執行

 

iOS中常見的鎖

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.