多線程 (三)iOS中的鎖

來源:互聯網
上載者:User

標籤:

鎖的類別:互斥鎖,遞迴鎖,條件鎖,自旋鎖等

鎖的實現方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的訊號量等

下面說一下常用的幾種鎖:

[email protected]:對象層級所,互斥鎖,效能較差不推薦使用

@synchronized(這裡添加一個OC對象,一般使用self) {

        這裡寫要加鎖的代碼

    }

  @synchronized使用注意點

  1.加鎖的代碼盡量少

  2.添加的OC對象必須在多個線程中都是同一對象,下面舉一個反例

- (void)viewDidLoad {    [super viewDidLoad];    //設定票的數量為5    _tickets = 5;    //線程一    NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];    threadOne.name = @"threadOne";    //線程二    NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];        //開啟線程    [threadOne start];    [threadTwo start]; }- (void)saleTickets{    NSObject *object = [[NSObject alloc] init];    while (1)    {        @synchronized(object) {            [NSThread sleepForTimeInterval:1];            if (_tickets > 0)            {                _tickets--;                NSLog(@"剩餘票數= %ld",_tickets);            }            else            {                NSLog(@"票賣完了");                break;            }        }    }    }

 結果賣票又出錯了,出現這個原因的問題是每個線程都會建立一個object對象,鎖後面加的object在不同線程中就不同了;

把@synchronized(object)改成 @synchronized(self)就能得到了正確結果

2.NSLock:互斥鎖,

@interface ViewController (){    NSLock *mutexLock;}@property (assign, nonatomic)NSInteger tickets;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    //建立鎖    mutexLock = [[NSLock alloc] init];        //設定票的數量為5    _tickets = 5;    //線程一    NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];    threadOne.name = @"threadOne";    //線程二    NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];        //開啟線程    [threadOne start];    [threadTwo start];  }- (void)saleTickets{    while (1)    {        [NSThread sleepForTimeInterval:1];        //加鎖        [mutexLock lock];        if (_tickets > 0)        {            _tickets--;            NSLog(@"剩餘票數= %ld",_tickets);        }        else        {            NSLog(@"票賣完了");            break;        }        //解鎖        [mutexLock unlock];        }}

 NSLock: 使用注意,不能多次調用 lock方法,會造成死結

 

3.NSRecursiveLock:遞迴鎖

@interface ViewController (){    NSRecursiveLock *rsLock;}@property (assign, nonatomic)NSInteger tickets;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    //建立鎖遞迴鎖    rsLock = [[NSRecursiveLock alloc] init];        //設定票的數量為5    _tickets = 5;    //線程一    NSThread *threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];    threadOne.name = @"threadOne";    //線程二    NSThread *threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(saleTickets) object:nil];    //開啟線程    [threadOne start];    [threadTwo start];    }- (void)saleTickets{    while (1)    {        [NSThread sleepForTimeInterval:1];        //加鎖,遞迴鎖可以多次加鎖        [rsLock lock];        [rsLock lock];        if (_tickets > 0)        {            _tickets--;            NSLog(@"剩餘票數= %ld",_tickets);        }        else        {            NSLog(@"票賣完了");            break;        }        //解鎖,只有對應次數解鎖,其他線程才能訪問。        [rsLock unlock];        [rsLock unlock];       }    }

 

4.NSConditionLock:條件鎖

 

NSConditionLock:條件鎖,一個線程獲得了鎖,其它線程等待。

 

 [xxxx lock]; 表示 xxx 期待獲得鎖,如果沒有其他線程獲得鎖(不需要判斷內部的condition) 那它能執行此行以下代碼,如果已經有其他線程獲得鎖(可能是條件鎖,或者無條件鎖),則等待,直至其他線程解鎖

 

 [xxx lockWhenCondition:A條件]; 表示如果沒有其他線程獲得該鎖,但是該鎖內部的condition不等於A條件,它依然不能獲得鎖,仍然等待。如果內部的condition等於A條件,並且沒有其他線程獲得該鎖,則進入代碼區,同時設定它獲得該鎖,其他任何線程都將等待它代碼的完成,直至它解鎖。

 

 [xxx unlockWithCondition:A條件]; 表示釋放鎖,同時把內部的condition設定為A條件

 

@interface ViewController () {       NSConditionLock *_cdtLock; //條件鎖}@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];        //建立條件鎖    _cdtLock = [[NSConditionLock alloc] init];    [NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];    [NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];    }- (void)conditionLockAction1 {        //阻塞線程2s    [NSThread sleepForTimeInterval:2];        for (NSInteger i = 0; i < 3; i++) {               //加鎖        [_cdtLock lock];                NSLog(@"i = %li", i);                //釋放鎖,並設定condition屬性的值為i        [_cdtLock unlockWithCondition:i];            }}- (void)conditionLockAction2 {        //當標識為2時同步程式碼片段才能夠執行,如果標識為其它數字則當前線程被阻塞。    [_cdtLock lockWhenCondition:2];        NSLog(@"thread2");        [_cdtLock unlock];    }

 列印結果:

如果我們把代碼中[_cdtLock lockWhenCondition:2]換成[_cdtLock lockWhenCondition:1]則會發現出現如下結果

和我們預想的在i = 1後面列印thread2不符合,這是因為conditionLockAction1中的程式碼片段也需要獲得鎖,同時在迴圈執行過後把condition置成了2,那麼conditionLockAction2就再也沒機會加鎖了,所以不列印thread2。

我們可以靠下面的代碼驗證

@interface ViewController () {       NSConditionLock *_cdtLock; //條件鎖}@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];        //建立條件鎖    _cdtLock = [[NSConditionLock alloc] init];    [NSThread detachNewThreadSelector:@selector(conditionLockAction1) toTarget:self withObject:nil];    [NSThread detachNewThreadSelector:@selector(conditionLockAction2) toTarget:self withObject:nil];    }- (void)conditionLockAction1 {        //阻塞線程2s    [NSThread sleepForTimeInterval:2];        for (NSInteger i = 0; i < 3; i++) {               //加鎖        [_cdtLock lock];                NSLog(@"i = %li", i);                //釋放鎖,並設定condition屬性的值為i        [_cdtLock unlockWithCondition:i];        //在i 為 1的時候阻塞線程1s        if (i == 1)        {            [NSThread sleepForTimeInterval:1];        }            }}- (void)conditionLockAction2 {        //當標識為2時同步程式碼片段才能夠執行,如果標識為其它數字則當前線程被阻塞。    [_cdtLock lockWhenCondition:1];        NSLog(@"thread2");        [_cdtLock unlock];    }

 現在的結果就和我們預期的一樣了

 

5.NSCondition:可以理解為互斥鎖和條件鎖的結合

 用生產者消費者中的例子可以很好的理解NSCondition

1.生產者要取得鎖,然後去生產,生產後將生產的商品放入庫房,如果庫房滿了,則wait,就釋放鎖,直到其它線程喚醒它去生產,如果沒有滿,則生產商品後調用signal,可以喚醒在此condition上等待的線程。

 2.消費者要取得鎖,然後去消費,如果當前沒有商品,則wait,釋放鎖,直到有線程去喚醒它消費,如果有商品,則消費後會通知正在等待的生產者去生產商品。

 生產者和消費者的關鍵是:當庫房已滿時,生產者等待,不再繼續生產商品,當庫房已空時,消費者等待,不再繼續消費商品,走到庫房有商品時,會由生產者通知消費來消費。

- (void)conditionTest{    //建立數組存放商品    products = [[NSMutableArray alloc] init];    condition = [[NSCondition alloc] init];        [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];    [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];}- (void)createConsumenr{    while (1) {        //類比消費商品時間,讓它比生產慢一點        [NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 1.5];        [condition lock];        while (products.count == 0) {            NSLog(@"商品為0,等待生產");            [condition wait];        }        [products removeLastObject];        NSLog(@"消費了一個商品,商品數 = %ld",products.count);        [condition signal];        [condition unlock];    }    }- (void)createProducter{    while (1) {        //類比生產商品時間        [NSThread sleepForTimeInterval:arc4random()%10 * 0.1 + 0.5];        [condition lock];        while (products.count == 5)        {            NSLog(@"商品滿了,等待消費");            [condition wait];        }        [products addObject:[[NSObject alloc] init]];        NSLog(@"生產了一個商品,商品數%ld",products.count);        [condition signal];        [condition unlock];    }    }

 另外發一張總結的圖片

多線程 (三)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.