談談iOS中的鎖(轉載)

原文地址

1 前言

近日工作不是太忙,剛好有時間了解一些其他東西,本來打算今天上午去體檢,但是看看天氣還是明天再去吧,也有很大一個原因:就是周六沒有預約上!閑話少說,這里簡單對鎖來個簡單介紹分享。

2 目錄

第一部分:什么是鎖

第二部分:鎖的分類

第三部分:鎖的作用

第四部分:iOS中鎖的實現

第一部分:什么是鎖

從小就知道鎖,就是家里門上的那個鎖,用來防止盜竊的鎖。它還有鑰匙,用于開鎖。不過這里的鎖,并不是小時候認知的鎖,而是站在程序員的角度的鎖。這里我就按照我的理解來介紹一下鎖。

在計算機科學中,鎖是一種同步機制,用于在存在多線程的環境中實施對資源的訪問限制。你可以理解成它用于排除并發的一種策略??蠢?/p>

1

2

3

4

5if(lock?==?0)?{

lock?=?myPID;

}

上面這段代碼并不能保證這個任務有個鎖,因此它可以在同一時間被多個任務執行。這個時候就有可能多個任務都檢測到lock是空閑的,因此兩個或者多個任務都將嘗試設置lock,而不知道其他的任務也在嘗試設置lock。這個時候就會出問題了。 再看看這段代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14classAcccount?{

longval?=?0;//這里不可在其他方法修改,只能通過add/minus修改

object?thisLock?=newobject();

publicvoidadd(constlongx)?{

lock(thisLock)?{

val?+=x;

}

}

publicvoidminus(constlongx)?{

lock(thisLock)?{

val?-=x;

}

}

}

這樣就能防止多個任務去修改val了,(這里注意,如果val是public的,那個也會導致一些問題)。

第二部分:鎖的分類

鎖根據不同的性質可以分成不同的類。

在WiKiPedia介紹中,一般的鎖都是建議鎖,也就四每個任務去訪問公共資源的時候,都需要取得鎖的資訊,再根據鎖資訊來確定是否可以存取。若存取對應資訊,鎖的狀態會改變為鎖定,因此其他線程不會訪問該資源,當結束訪問時,鎖會釋放,允許其他任務訪問。有些系統有強制鎖,若未經授權的鎖訪問鎖定的資料,在訪問時就會產生異常。

在iOS中,鎖分為遞歸鎖、條件鎖、分布式鎖、一般鎖(這里是看著NSLock類里面的分類劃分的)。

對于數據庫的鎖分類:

分類方式分類

按鎖的粒度劃分表級鎖、行級鎖、頁級鎖

按鎖的級別劃分共享鎖、排他鎖

按加鎖方式劃分自動鎖、顯示鎖

按鎖的使用方式劃分樂觀鎖、悲觀鎖

按操作劃分DML鎖、DDL鎖

這里就不在詳細介紹了,感興趣的大家可以自己查閱相關資料。

第三部分:鎖的作用

這個比較通俗來講:就是為了防止在多線程(多任務)的情況下對共享資源(臨界資源)的臟讀或者臟寫。也可以理解為:執行多線程時用于強行限制資源訪問的同步機制,即并發控制中保證互斥的要求。

第四部分:iOS中鎖的實現

先看看iOS中NSLock類的.h文件。這里就不在寫上來了。從代碼中可以看出,該類分成了幾個子類:NSLock、NSConditionLock、NSRecursiveLock以及NSCondition。然后有一個NSLocking的協議:

1

2

3

4

5

6

7@protocol?NSLocking

-?(void)lock;

-?(void)unlock;

@end

這幾個子類都遵循了NSLock的協議,這里簡單介紹一下其中的幾個方法: 對于tryLock方法,嘗試獲取一個鎖,并且立刻返回Bool值,YES表示獲取了鎖,NO表示沒有獲取鎖失敗。 lockBeforeDate:方法,在某個時刻之前獲取鎖,如果獲取成功,則返回YES,NO表示獲取鎖失敗。接下來就讓我們看一下iOS中實現鎖的方式:

方式1 使用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-?(void)nslockDemo?{

NSLock?*myLock?=?[[NSLock?alloc]?init];

_testLock?=?[[TestLock?alloc]?init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

[myLock?lock];

[_testLock?method1];

sleep(5);

[myLock?unlock];

if([myLock?tryLock])?{

NSLog(@"可以獲得鎖");

}else{

NSLog(@"不可以獲得所");

}

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

sleep(1);

if([myLock?tryLock])?{

NSLog(@"---可以獲得鎖");

}else{

NSLog(@"----不可以獲得所");

}

[myLock?lock];

[_testLock?method2];

[myLock?unlock];

});

}

方式2 使用@synchorize

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16-?(void)synchronizeDemo?{

_testLock?=?[[TestLock?alloc]?init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

@synchronized?(_testLock)?{

[_testLock?method1];

sleep(5);

}

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

sleep(1);

@synchronized?(_testLock)?{

[_testLock?method2];

}

});

}

對于@synchorize指令中使用的testLock為該鎖標示,只有標示相同的時候才滿足鎖的效果。它的優點是不用顯式地創建鎖,便可以實現鎖的機制。但是它會隱式地添加異常處理程序來保護代碼,該程序在拋出異常的時候自動釋放鎖。

方式3 使用gcd

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16-?(void)gcdDemo?{

_testLock?=?[[TestLock?alloc]?init];

dispatch_semaphore_t?semaphore?=?dispatch_semaphore_create(1);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

dispatch_semaphore_wait(semaphore,?DISPATCH_TIME_FOREVER);

[_testLock?method1];

sleep(5);

dispatch_semaphore_signal(semaphore);

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

sleep(1);

dispatch_semaphore_wait(semaphore,?DISPATCH_TIME_FOREVER);

[_testLock?method2];

dispatch_semaphore_signal(semaphore);

});

}

方式4 使用phtread

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22-?(void)pthreadDemo?{

_testLock?=?[[TestLock?alloc]?init];

__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);

[_testLock?method1];

sleep(5);

pthread_mutex_unlock(&mutex);

});

//線程2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

sleep(1);

pthread_mutex_lock(&mutex);

[_testLock?method2];

pthread_mutex_unlock(&mutex);

});

}

pthread_mutex_t定義在pthread.h,所以記得#include。

3 性能對比

這里簡單寫一個小程序來進行四種方式的性能對比,這里再固定次數內進行了加鎖解鎖,然后輸出用時,結果如下(測試1、2執行次數不一樣:測試1 < 測試2):

1

2

3

4

5

6

7

8

9

10

11

12

13

14測試1

2016-11-05?15:27:52.595?LockDemo[4394:202297]?NSLocktimes:0.871843

2016-11-05?15:27:56.335?LockDemo[4394:202297]?synthorizetimes:3.738939

2016-11-05?15:27:56.691?LockDemo[4394:202297]?gcdtimes:0.355344

2016-11-05?15:27:57.328?LockDemo[4394:202297]?pthreadtimes:0.636815

2016-11-05?15:27:57.559?LockDemo[4394:202297]?OSSPinLocktimes:0.231013

2016-11-05?15:27:57.910?LockDemo[4394:202297]?os_unfair_locktimes:0.350615

測試2

2016-11-05?15:30:54.123?LockDemo[4454:205180]?NSLocktimes:1.908103

2016-11-05?15:31:02.112?LockDemo[4454:205180]?synthorizetimes:7.988547

2016-11-05?15:31:02.905?LockDemo[4454:205180]?gcdtimes:0.792113

2016-11-05?15:31:04.372?LockDemo[4454:205180]?pthreadtimes:1.466987

2016-11-05?15:31:04.870?LockDemo[4454:205180]?OSSPinLocktimes:0.497487

2016-11-05?15:31:05.637?LockDemo[4454:205180]?os_unfair_locktimes:0.767569

這里還測試了OSSPinLock(此類已經被os_unfair_lock所替代)。結果如下: synthorize > NSLock > pthread > gcd > os_unfair_lock >OSSPinLock 這里:

synthorize內部會添加異常處理,所以耗時。

pthread_mutex底層API,處理能力不錯。

gcd系統封裝的C代碼效果比pthread好。

4 總結

簡單就介紹這么多。

5 參考文檔:

http://www.liuhaihua.cn/archives/220300.html

https://zh.wikipedia.org/zh-hans/%E9%94%81_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)

https://en.wikipedia.org/wiki/Lock_(computer_science)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容