天天看點

iOS多線程中的鎖

鎖的類别:互斥鎖,遞歸鎖,條件鎖,自旋鎖等

鎖的實作方式:NSLock,NSRecursiveLock, NSConditionLock,@synchronized,GCD的信号量等

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

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

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

        這裡寫要加鎖的代碼

    }

  @synchronized使用注意點

  1.加鎖的代碼盡量少

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

- (

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在不同線程中就不同了;

iOS多線程中的鎖

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

iOS多線程中的鎖

2.NSLock:互斥鎖,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

@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:遞歸鎖

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

@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條件

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

@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];

}

 列印結果:

iOS多線程中的鎖

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

iOS多線程中的鎖

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53

@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];

}

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

iOS多線程中的鎖

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

 用生産者消費者中的例子可以很好的了解NSCondition

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

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

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

- (

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];

}

}

了解死鎖

  概念:死鎖是指兩個或兩個以上的程序(線程)在執行過程中,由于競争資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都将無法推進下去。

産生死鎖的4個必要條件

1)互斥條件:指程序對所配置設定到的資源進行排它性使用,即在一段時間内某資源隻由一個程序占用。如果此時還有其它程序請求資源,則請求者隻能等待,直至占有資源的程序用畢釋放。

2)請求和保持條件:指程序已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它程序占有,此時請求程序阻塞,但又對自己已獲得的其它資源保持不放。

3)不剝奪條件:指程序已獲得的資源,在未使用完之前,不能被剝奪,隻能在使用完時由自己釋放。

4)環路等待條件:指在發生死鎖時,必然存在一個程序——資源的環形鍊,即程序集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。

繼續閱讀