鎖是常用的同步工具桃序。一段代碼段在同一個(gè)時(shí)間只能允許被有限個(gè)線程訪問,比如一個(gè)線程 A 進(jìn)入需要保護(hù)的代碼之前添加簡單的互斥鎖,另一個(gè)線程 B 就無法訪問這段保護(hù)代碼了舶赔,只有等待前一個(gè)線程 A 執(zhí)行完被保護(hù)的代碼后解鎖湃交,B 線程才能訪問被保護(hù)的代碼段熟空。本篇就來總結(jié)這些 iOS 開發(fā)中使用到的鎖,包括 spinlock_t搞莺、os_unfair_lock息罗、pthread_mutex_t、NSLock才沧、NSRecursiveLock迈喉、NSCondition、NSConditionLock温圆、@synchronized挨摸、dispatch_semaphore、pthread_rwlock_t岁歉。
spinlock_t
自旋鎖得运,也只有加鎖、解鎖和嘗試加鎖三個(gè)方法锅移。和 NSLock 不同的是 NSLock 請(qǐng)求加鎖失敗的話熔掺,會(huì)先輪詢,但一秒后便會(huì)使線程進(jìn)入 waiting 狀態(tài)非剃,等待喚醒置逻。而 OSSpinLock 會(huì)一直輪詢,等待時(shí)會(huì)消耗大量 CPU 資源努潘,不適用于較長時(shí)間的任務(wù)诽偷。
?使用 OSSpinLock 需要先引入 #import <libkern/OSAtomic.h>坤学。看到 usr/include/libkern/OSSpinLockDeprecated.h 名字后面的 Deprecated 強(qiáng)烈的提示著我們 OSSpinLock 已經(jīng)不贊成使用了报慕。
?查看 OSSpinLockDeprecated.h 文件內(nèi)容 OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock) 提示我們使用 os_unfair_lock 代替 OSSpinLock深浮。
?OSSpinLock 存在線程安全問題,它可能導(dǎo)致優(yōu)先級(jí)反轉(zhuǎn)問題眠冈,目前我們?cè)谌魏吻闆r下都不應(yīng)該再使用它飞苇,我們可以使用 apple 在 iOS 10.0 后推出的 os_unfair_lock (作為 OSSpinLock 的替代) 。關(guān)于 os_unfair_lock 我們下一節(jié)展開學(xué)習(xí)蜗顽。
OSSpinLock API 簡單使用
OSSpinLock API
很簡單布卡,首先看下使用示例。
#import "ViewController.h"
#import <libkern/OSAtomic.h> // 引入 OSSpinLock
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) OSSpinLock lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.lock = OS_SPINLOCK_INIT; // 初始化鎖
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 取得一個(gè)全局并發(fā)隊(duì)列
self.sum = 0; // sum 從 0 開始
dispatch_async(globalQueue, ^{ // 異步任務(wù) 1
OSSpinLockLock(&_lock); // 獲得鎖
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
NSLog(@"??? %ld", self.sum);
OSSpinLockUnlock(&_lock); // 解鎖
});
dispatch_async(globalQueue, ^{ // 異步任務(wù) 2
OSSpinLockLock(&_lock); // 獲得鎖
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
NSLog(@"?????? %ld", self.sum);
OSSpinLockUnlock(&_lock); // 解鎖
});
}
@end
// 打印 ???:
??? 10000
?????? 20000
// 把 lock 注釋后雇盖,運(yùn)行多次可總結(jié)出如下三種不同情況忿等,
// 其中只有一個(gè)任務(wù)達(dá)到了 10000 以上另一個(gè)是 10000 以下,另一種是兩者都達(dá)到了 10000 以上
// 情況 1:
??? 9064
?????? 13708
// 情況 2:
??? 11326
?????? 9933
// 情況 3:
??? 10906
?????? 11903
...
sum 屬性使用 atomic 或 nonatomic 時(shí)結(jié)果相同崔挖,atomic 雖是原子操作贸街,但它不是線程安全的,它的原子性只是限于對(duì)它所修飾的變量在 setter 和 getter 時(shí)加鎖而已狸相,當(dāng)遇到 self.sum++ 或者 self.sum = self.sum + 1 等等這種復(fù)合操作時(shí)薛匪,atomic 是完全保證不了線程安全的。
在不加鎖情況下打印的數(shù)字有一些有趣的點(diǎn)脓鹃,這里分析一下:(假設(shè)在全局并發(fā)隊(duì)列下的兩個(gè) dispatch_async
任務(wù)都開啟了新線程逸尖,并把兩條線分別命名為 “?線程” 和 “??線程”)
1.可以確定的是 ?線程 和 ??線程 不會(huì)有任何一個(gè)可以打印 20000
。
2.?線程 和 ??線程 兩者的打印都到了10000
以上瘸右。
3.?線程 或 ??線程 其中一個(gè)打印在 10000
以上一個(gè)在 10000
以下娇跟。
情況 1 我們都能想到,因?yàn)??線程 和 ??線程 是并發(fā)進(jìn)行的尊浓,不會(huì)存在一個(gè)線程先把 sum 自增到
10000
然后另一個(gè)線程再把sum
自增到20000
逞频,只有加鎖或者self.sum
自增的任務(wù)在串行隊(duì)列中執(zhí)行才行纯衍。
情況 2 我們可能也好理解栋齿,兩者都打印到
10000
以上,可以分析為某個(gè)時(shí)間點(diǎn) ?線程 持續(xù)自增襟诸,然后 ??線程 在這個(gè)時(shí)間點(diǎn)后執(zhí)行循環(huán)時(shí) sum 已經(jīng)大于它上一次循環(huán)時(shí)的值了瓦堵,然后 ?線程 和 ??線程 下sum
的值都是以大于其上一次循環(huán)的值往下繼續(xù)循環(huán),最后兩條線程的打印sum
值都是大于10000
的歌亲。
情況 3 則理解比較麻煩菇用,為什么其中一個(gè)可以小于
10000
,可能是其中一個(gè)線程執(zhí)行忽快忽慢造成的嗎陷揪? 還有如果被縮小一次惋鸥,那不是會(huì)導(dǎo)致兩條線程最終打印sum
都會(huì)小于10000
嗎杂穷?可能是self.sum
讀取時(shí)是從寄存器或內(nèi)存中讀取造成的嗎?想到了volatile
關(guān)鍵字卦绣。(暫時(shí)先分析到這里耐量,分析不下去了)
OSSpinLockDeprecated.h 文件內(nèi)容
下面直接查看 OSSpinLockDeprecated.h
中的代碼內(nèi)容
?
上面示例代碼中每一行與 OSSpinLock
相關(guān)的代碼都會(huì)有這樣一行警告 ????'OSSpinLock' is deprecated: first deprecated in iOS 10.0 - Use os_unfair_lock() from <os/lock.h> instead
。正是由下面的 OSSPINLOCK_DEPRECATED
所提示滤港,在 4 大系統(tǒng)中都提示我們都不要再用 OSSpinLock
了廊蜒。
#ifndef OSSPINLOCK_DEPRECATED
#define OSSPINLOCK_DEPRECATED 1
#define OSSPINLOCK_DEPRECATED_MSG(_r) "Use " #_r "() from <os/lock.h> instead"
#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r) \
__OS_AVAILABILITY_MSG(macosx, deprecated=10.12, OSSPINLOCK_DEPRECATED_MSG(_r)) \
__OS_AVAILABILITY_MSG(ios, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \
__OS_AVAILABILITY_MSG(tvos, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \
__OS_AVAILABILITY_MSG(watchos, deprecated=3.0, OSSPINLOCK_DEPRECATED_MSG(_r))
#else
#undef OSSPINLOCK_DEPRECATED
#define OSSPINLOCK_DEPRECATED 0
#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r)
#endif
下面是不同情況下的 OSSpinLock API
實(shí)現(xiàn):
1.#if !(defined(OSSPINLOCK_USE_INLINED) && OSSPINLOCK_USE_INLINED)
為真不使用內(nèi)聯(lián)時(shí)的原始 API:
-
#define OS_SPINLOCK_INIT 0
初始化。
/*! @abstract The default value for an <code>OSSpinLock</code>. OSSpinLock 的默認(rèn)值是 0(unlocked 狀態(tài))
@discussion
The convention is that unlocked is zero, locked is nonzero. 慣例是: unlocked 時(shí)是零溅漾,locked 時(shí)時(shí)非零
*/
#define OS_SPINLOCK_INIT 0
-
OSSpinLock
數(shù)據(jù)類型山叮。
/*! @abstract Data type for a spinlock. 自旋鎖的數(shù)據(jù)類型是 int32_t
@discussion
You should always initialize a spinlock to {@link OS_SPINLOCK_INIT} before using it.
在使用一個(gè)自旋鎖之前,我們應(yīng)該總是先把它初始化為 OS_SPINLOCK_INIT添履。(其實(shí)是對(duì)它賦值為數(shù)字 0)
*/
typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock);
-
OSSpinLockTry
嘗試加鎖屁倔,bool
類型的返回值表示是否加鎖成功,即使加鎖失敗也不會(huì)阻塞線程暮胧。
/*! @abstract Locks a spinlock if it would not block. 如果一個(gè) spinlock 未鎖定汰现,則鎖定它。
@result
Returns <code>false</code> if the lock was already held by another thread,
<code>true</code> if it took the lock successfully.
如果鎖已經(jīng)被另一個(gè)線程所持有則返回 false叔壤,否則返回 true 表示加鎖成功瞎饲。
*/
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
bool OSSpinLockTry( volatile OSSpinLock *__lock );
-
OSSpinLockLock
加鎖。
/*! @abstract Locks a spinlock. 鎖定一個(gè) spinlock
@discussion
Although the lock operation spins, it employs various strategies to back
off if the lock is held.
盡管鎖定操作旋轉(zhuǎn)炼绘,(當(dāng)加鎖失敗時(shí)會(huì)一直處于等待狀態(tài)嗅战,一直到獲取到鎖為止,獲取到鎖之前會(huì)一直處于阻塞狀態(tài))
它采用各種策略來支持如果加鎖成功俺亮,則關(guān)閉旋轉(zhuǎn)驮捍。
*/
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
void OSSpinLockLock( volatile OSSpinLock *__lock );
-
OSSpinLockUnlock
解鎖。
/*! @abstract Unlocks a spinlock */
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock)
__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0)
void OSSpinLockUnlock( volatile OSSpinLock *__lock );
2.OSSPINLOCK_USE_INLINED
為真使用內(nèi)聯(lián)脚曾,內(nèi)聯(lián)實(shí)現(xiàn)是使用 os_unfair_lock_t
代替 OSSpinLock
东且。
Inline implementations of the legacy OSSpinLock interfaces in terms of the of the <os/lock.h> primitives. Direct use of those primitives is preferred.
?NOTE: the locked value of os_unfair_lock is implementation defined and subject to change, code that relies on the specific locked value used by the legacy OSSpinLock interface WILL break when using these inline implementations in terms of os_unfair_lock.
就 <os/lock.h> 中的原始接口而言,此處是原始 OSSpinLock 接口的內(nèi)聯(lián)實(shí)現(xiàn)本讥。最好直接使用這些 primitives珊泳。
?NOTE: os_unfair_lock 的鎖定值是實(shí)現(xiàn)定義的,可能會(huì)更改拷沸。當(dāng)使用這些內(nèi)聯(lián)實(shí)現(xiàn)時(shí)色查,依賴于舊版 OSSpinLock 接口使用的特定鎖定值的代碼會(huì)中斷 os_unfair_lock。
在函數(shù)前加 OSSPINLOCK_INLINE
告訴編譯器盡最大努力保證被修飾的函數(shù)內(nèi)聯(lián)實(shí)現(xiàn)撞芍。
#if __has_attribute(always_inline) // 盡最大努力保證函數(shù)內(nèi)聯(lián)實(shí)現(xiàn)
#define OSSPINLOCK_INLINE static __inline
#else
#define OSSPINLOCK_INLINE static __inline __attribute__((__always_inline__))
#endif
#define OS_SPINLOCK_INIT 0 // 初始化為 0
typedef int32_t OSSpinLock; // 類型依然是 int32_t
#if __has_extension(c_static_assert)
// 如果 OSSpinLock 和 os_unfair_lock 內(nèi)存長度不同秧了,即類型不兼容,不能保證雙方能正確的轉(zhuǎn)換序无,直接斷言验毡。
_Static_assert(sizeof(OSSpinLock) == sizeof(os_unfair_lock), "Incompatible os_unfair_lock type");
#endif
-
os_unfair_lock
加鎖衡创。
OSSPINLOCK_INLINE
void
OSSpinLockLock(volatile OSSpinLock *__lock)
{
// 轉(zhuǎn)換為 os_unfair_lock_t。
os_unfair_lock_t lock = (os_unfair_lock_t)__lock;
return os_unfair_lock_lock(lock);
}
-
os_unfair_lock
嘗試加鎖晶通。
OSSPINLOCK_INLINE
bool
OSSpinLockTry(volatile OSSpinLock *__lock)
{
// 轉(zhuǎn)換為 os_unfair_lock_t钧汹。
os_unfair_lock_t lock = (os_unfair_lock_t)__lock;
return os_unfair_lock_trylock(lock);
}
-
os_unfair_lock
解鎖。
OSSPINLOCK_INLINE
void
OSSpinLockUnlock(volatile OSSpinLock *__lock)
{
// 轉(zhuǎn)換為 os_unfair_lock_t录择。
os_unfair_lock_t lock = (os_unfair_lock_t)__lock;
return os_unfair_lock_unlock(lock);
}
#undef OSSPINLOCK_INLINE
解除上面的宏定義拔莱。
3.最后一種情況。
#define OS_SPINLOCK_INIT 0 // 初始化
typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock); // 類型 int32_t
typedef volatile OSSpinLock *_os_nospin_lock_t
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_t); // 命名 _os_nospin_lock_t
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock)
OS_NOSPIN_LOCK_AVAILABILITY
void _os_nospin_lock_lock(_os_nospin_lock_t lock); // 加鎖
#undef OSSpinLockLock // 解除上面的原始 API 的加鎖的宏定義
#define OSSpinLockLock(lock) _os_nospin_lock_lock(lock)
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock)
OS_NOSPIN_LOCK_AVAILABILITY
bool _os_nospin_lock_trylock(_os_nospin_lock_t lock); // 嘗試加鎖
#undef OSSpinLockTry // 解除上面的原始 API 的判斷能否加鎖的宏定義
#define OSSpinLockTry(lock) _os_nospin_lock_trylock(lock)
OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock)
OS_NOSPIN_LOCK_AVAILABILITY
void _os_nospin_lock_unlock(_os_nospin_lock_t lock); // 解鎖
#undef OSSpinLockUnlock // 解除上面的原始 API 的解鎖的宏定義
#define OSSpinLockUnlock(lock) _os_nospin_lock_unlock(lock)
至此 OSSpinLockDeprecated.h 文件代碼結(jié)束隘竭,整體而言只有 4 條塘秦。
1.
OS_SPINLOCK_INIT
初始化。
2.
OSSpinLockTry()
嘗試加鎖动看,如果鎖已經(jīng)被另一個(gè)線程所持有則返回 false尊剔,否則返回 true,即使加鎖失敗也不會(huì)阻塞當(dāng)前線程菱皆。
3.
OSSpinLockLock()
加鎖须误,加鎖失敗會(huì)一直等待,會(huì)阻塞當(dāng)前線程仇轻。
4.
OSSpinLockUnlock
解鎖京痢。
OSSpinLock 的安全問題
自旋鎖
OSSpinLock
不是一個(gè)線程安全的鎖,等待鎖的線程會(huì)處于忙等(busy-wait
)狀態(tài)篷店,一直占用著CPU
資源祭椰。(類似一個(gè)while(1)
循環(huán)一樣,不停的查詢鎖的狀態(tài)疲陕,注意區(qū)分runloop
的機(jī)制方淤,同樣是阻塞,但是runloop
是類似休眠的阻塞蹄殃,不會(huì)耗費(fèi)CPU
資源携茂,自旋鎖的這種忙等機(jī)制使它相比其它鎖效率更高,畢竟沒有喚醒-休眠這些類似操作诅岩,從而能更快的處理事情讳苦。)自旋鎖目前已經(jīng)被廢棄了,它可能會(huì)導(dǎo)致優(yōu)先級(jí)反轉(zhuǎn)按厘。
例如
A/B
兩個(gè)線程医吊,A
的優(yōu)先級(jí)大于B
的钱慢,我們的本意是A
的任務(wù)優(yōu)先執(zhí)行逮京,但是使用OSSpinLock
后,如果是B
優(yōu)先訪問了共享資源獲得了鎖并加鎖束莫,而A
線程再去訪問共享資源的時(shí)候鎖就會(huì)處于忙等狀態(tài)懒棉,由于A
的優(yōu)先級(jí)高它會(huì)一直占用CPU
資源不會(huì)讓出時(shí)間片草描,這樣B
一直不能獲得CPU
資源去執(zhí)行任務(wù),導(dǎo)致無法完成策严。
《不再安全的 OSSpinLock》
原文: 新版 iOS 中穗慕,系統(tǒng)維護(hù)了 5 個(gè)不同的線程優(yōu)先級(jí)/QoS: background,utility妻导,default逛绵,user-initiated,user-interactive倔韭。高優(yōu)先級(jí)線程始終會(huì)在低優(yōu)先級(jí)線程前執(zhí)行术浪,一個(gè)線程不會(huì)受到比它更低優(yōu)先級(jí)線程的干擾。這種線程調(diào)度算法會(huì)產(chǎn)生潛在的優(yōu)先級(jí)反轉(zhuǎn)問題寿酌,從而破壞了 spin lock胰苏。
具體來說,如果一個(gè)低優(yōu)先級(jí)的線程獲得鎖并訪問共享資源醇疼,這時(shí)一個(gè)高優(yōu)先級(jí)的線程也嘗試獲得這個(gè)鎖硕并,它會(huì)處于 spin lock 的忙等狀態(tài)從而占用大量 CPU。此時(shí)低優(yōu)先級(jí)線程無法與高優(yōu)先級(jí)線程爭奪 CPU 時(shí)間秧荆,從而導(dǎo)致任務(wù)遲遲完不成倔毙、無法釋放 lock。這并不只是理論上的問題乙濒,libobjc 已經(jīng)遇到了很多次這個(gè)問題了普监,于是蘋果的工程師停用了 OSSpinLock。 蘋果工程師 Greg Parker 提到琉兜,對(duì)于這個(gè)問題凯正,一種解決方案是用 truly unbounded backoff 算法,這能避免 livelock 問題豌蟋,但如果系統(tǒng)負(fù)載高時(shí)廊散,它仍有可能將高優(yōu)先級(jí)的線程阻塞數(shù)十秒之久;另一種方案是使用 handoff lock 算法梧疲,這也是 libobjc 目前正在使用的允睹。鎖的持有者會(huì)把線程 ID 保存到鎖內(nèi)部,鎖的等待者會(huì)臨時(shí)貢獻(xiàn)出它的優(yōu)先級(jí)來避免優(yōu)先級(jí)反轉(zhuǎn)的問題幌氮。理論上這種模式會(huì)在比較復(fù)雜的多鎖條件下產(chǎn)生問題缭受,但實(shí)踐上目前還一切都好。 libobjc 里用的是 Mach 內(nèi)核的 thread_switch() 然后傳遞了一個(gè) mach thread port 來避免優(yōu)先級(jí)反轉(zhuǎn)该互,另外它還用了一個(gè)私有的參數(shù)選項(xiàng)米者,所以開發(fā)者無法自己實(shí)現(xiàn)這個(gè)鎖。另一方面,由于二進(jìn)制兼容問題蔓搞,OSSpinLock 也不能有改動(dòng)胰丁。 最終的結(jié)論就是,除非開發(fā)者能保證訪問鎖的線程全部都處于同一優(yōu)先級(jí)喂分,否則 iOS 系統(tǒng)中所有類型的自旋鎖都不能再使用了锦庸。-《不再安全的 OSSpinLock》
os_unfair_lock
os_unfair_lock
設(shè)計(jì)宗旨是用于替換OSSpinLock
,從iOS 10
之后開始支持蒲祈,跟OSSpinLock
不同甘萧,等待os_unfair_lock
的線程會(huì)處于休眠狀態(tài)(類似Runloop
那樣),不是忙等(busy-wait
)梆掸。
os_unfair_lock 引子
看到 struct SideTable
定義中第一個(gè)成員變量是 spinlock_t slock
;幔嗦, 這里展開對(duì) spinlock_t
的學(xué)習(xí)。
struct SideTable {
spinlock_t slock;
...
};
spinlock_t
其實(shí)是使用 using
聲明的一個(gè)模版類沥潭。
#if DEBUG
# define LOCKDEBUG 1
#else
# define LOCKDEBUG 0
#endif
template <bool Debug> class mutex_tt;
using spinlock_t = mutex_tt<LOCKDEBUG>;
所以 spinlock_t
其實(shí)是一個(gè)互斥鎖邀泉,與它的名字自旋鎖是不符的,其實(shí)以前它是OSSpinLock
钝鸽,因?yàn)槠鋬?yōu)先級(jí)反轉(zhuǎn)導(dǎo)致的安全問題而被遺棄了汇恤。
template <bool Debug>
class mutex_tt : nocopy_t { // 繼承自 nocopy_t
os_unfair_lock mLock;
public:
constexpr mutex_tt() : mLock(OS_UNFAIR_LOCK_INIT) {
lockdebug_remember_mutex(this);
}
...
};
nocopy_t
正如其名,刪除編譯器默認(rèn)生成的復(fù)制構(gòu)造函數(shù)和賦值操作符拔恰,而構(gòu)造函數(shù)和析構(gòu)函數(shù)則依然使用編譯器默認(rèn)生成的因谎。
// Mix-in for classes that must not be copied.
// 構(gòu)造函數(shù) 和 析構(gòu)函數(shù) 使用編譯器默認(rèn)生成的,刪除 復(fù)制構(gòu)造函數(shù) 和 賦值操作符颜懊。
class nocopy_t {
private:
nocopy_t(const nocopy_t&) = delete;
const nocopy_t& operator=(const nocopy_t&) = delete;
protected:
constexpr nocopy_t() = default;
~nocopy_t() = default;
};
mute_tt
類的第一個(gè)成員變量是: os_unfair_lock mLock
财岔。
os_unfair_lock 正片
在 usr/include/os/lock.h
中看到 os_unfair_lock
的定義,使用 os_unfair_lock
首先需要引入 #import <os/lock.h>
河爹。
os_unfair_lock API 簡單使用
os_unfair_lock API
很簡單匠璧,首先看下使用示例。
#import "ViewController.h"
#import <os/lock.h> // os_unfair_lock
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) os_unfair_lock unfairL;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_sum = 100; // 只是給自動(dòng)生成的 _sum 成員變量賦值咸这,不會(huì)調(diào)用 sum 的 setter 函數(shù)
self->_sum = 1000; // 只是給自動(dòng)生成的 _sum 成員變量賦值夷恍,不會(huì)調(diào)用 sum 的 setter 函數(shù)
// 一定要區(qū)分 self. 中的 .,它和 C/C++ 中的 . 是不一樣的媳维,OC 中的 . 是調(diào)用 getter/setter 函數(shù)酿雪。
// 開始一直疑惑 self.xxx,self 是一個(gè)指針侄刽,不是應(yīng)該使用 self->xxx 嗎?
// 在 OC 中指黎,應(yīng)該是 self->_xxx,_xxx 是 xxx 屬性自動(dòng)生成的對(duì)應(yīng)的成員變量 _xxx
// self 是一個(gè)結(jié)構(gòu)體指針州丹,所以訪問指針的成員變量醋安,只能是 self->_xxx,不能是 self->xxx
// 等號(hào)左邊的 "self.unfairL = xxx" 相當(dāng)于調(diào)用 unfairL 的 setter 函數(shù)給它賦值
// 即 [self setUnfairL:OS_UNFAIR_LOCK_INIT];
// 等號(hào)右邊的 "xxx = self.unfaiL" 或者 "self.unfairL" 的使用,
// 相當(dāng)于調(diào)用 unfairL 的 getter 函數(shù)茬故,讀取它的值
// 相當(dāng)于調(diào)用 getter 函數(shù):[self unfairL]
/*
// os_unfair_lock 是一個(gè)結(jié)構(gòu)體
typedef struct os_unfair_lock_s {
uint32_t _os_unfair_lock_opaque;
} os_unfair_lock, *os_unfair_lock_t;
*/
self.unfairL = OS_UNFAIR_LOCK_INIT; // 初始化
dispatch_queue_t globalQueue_DEFAULT = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
self.sum = 0;
__weak typeof(self) _self = self;
dispatch_async(globalQueue_DEFAULT, ^{
__strong typeof(_self) self = _self;
// 不是使用 &self.unfairL盖灸,
// 這樣使用相當(dāng)于 &[self unfairL]
// 不能這樣取地址
// &[self unfairL]蚁鳖,
// 報(bào)錯(cuò): Cannot take the address of an rvalue of type 'os_unfair_lock'
// 報(bào)錯(cuò): 不能獲取類型為 "os_unfair_lock" 的右值的地址
// &self.unfairL;
// 報(bào)錯(cuò): Address of property expression requested
// 只能使用 &self->_unfairL
// 先拿到成員變量 _unfairL磺芭,然后再取地址
os_unfair_lock_lock(&self->_unfairL); // 加鎖
// os_unfair_lock_lock(&self->_unfairL); // 重復(fù)加鎖會(huì)直接 crash
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
os_unfair_lock_unlock(&self->_unfairL); // 解鎖
NSLog(@"??? %ld", self.sum);
});
dispatch_async(globalQueue_DEFAULT, ^{
__strong typeof(_self) self = _self;
os_unfair_lock_lock(&self->_unfairL); // 加鎖
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
os_unfair_lock_unlock(&self->_unfairL); // 解鎖
NSLog(@"?????? %ld", self.sum);
});
}
@end
// 打印:
?????? 10000
??? 20000
lock.h 文件內(nèi)容
首先是一個(gè)宏定義告訴我們 os_unfair_lock
出現(xiàn)的時(shí)機(jī)∽砘看到os_unfair_lock
是在iOS 10.0
以后首次出現(xiàn)的钾腺。
#define OS_LOCK_API_VERSION 20160309
#define OS_UNFAIR_LOCK_AVAILABILITY \
__API_AVAILABLE(macos(10.12), ios(10.0), tvos(10.0), watchos(3.0))
/*!
@typedef os_unfair_lock
@abstract
Low-level lock that allows waiters to block efficiently on contention.
In general, higher level synchronization primitives such as those provided by
the pthread or dispatch subsystems should be preferred.
The values stored in the lock should be considered opaque and implementation
defined, they contain thread ownership information that the system may use
to attempt to resolve priority inversions.
This lock must be unlocked from the same thread that locked it, attempts to
unlock from a different thread will cause an assertion aborting the process.
This lock must not be accessed from multiple processes or threads via shared
or multiply-mapped memory, the lock implementation relies on the address of
the lock value and owning process.
Must be initialized with OS_UNFAIR_LOCK_INIT
@discussion
Replacement for the deprecated OSSpinLock. Does not spin on contention but
waits in the kernel to be woken up by an unlock.
As with OSSpinLock there is no attempt at fairness or lock ordering, e.g. an
unlocker can potentially immediately reacquire the lock before a woken up
waiter gets an opportunity to attempt to acquire the lock. This may be
advantageous for performance reasons, but also makes starvation of waiters a
possibility.
*/
對(duì)以上摘要內(nèi)容進(jìn)行總結(jié),大概包括以下 幾 點(diǎn):
1.
os_unfair_lock
是一個(gè)低等級(jí)鎖讥裤。一些高等級(jí)的鎖才應(yīng)該是我們?nèi)粘i_發(fā)中的首選放棒。
2.必須使用加鎖時(shí)的同一個(gè)線程來進(jìn)行解鎖,嘗試使用不同的線程來解鎖將導(dǎo)致斷言中止進(jìn)程己英。
3.鎖里面包含線程所有權(quán)信息來解決優(yōu)先級(jí)反轉(zhuǎn)問題间螟。
4.不能通過共享或多重映射內(nèi)存從多個(gè)進(jìn)程或線程訪問此鎖,鎖的實(shí)現(xiàn)依賴于鎖值的地址和所屬進(jìn)程损肛。
5.必須使用 OS_UNFAIR_LOCK_INIT 進(jìn)行初始化厢破。
os_unfair_lock_s
結(jié)構(gòu),typedef 定義別名治拿,os_unfair_lock
是一個(gè)os_unfair_lock_s
結(jié)構(gòu)體摩泪,os_unfair_lock_t
是一個(gè) os_unfair_lock_s
指針,該結(jié)構(gòu)體內(nèi)部就一個(gè)uint32_t _os_unfair_lock_opaque
成員變量劫谅。
OS_UNFAIR_LOCK_AVAILABILITY
typedef struct os_unfair_lock_s {
uint32_t _os_unfair_lock_opaque;
} os_unfair_lock, *os_unfair_lock_t;
針對(duì)不同的平臺(tái)或者 C++
版本以不同的方式來進(jìn)行初始化 (os_unfair_lock){0}
见坑。
1.(os_unfair_lock){0}
2.os_unfair_lock{}
3.os_unfair_lock()
4.{0}
#ifndef OS_UNFAIR_LOCK_INIT
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define OS_UNFAIR_LOCK_INIT ((os_unfair_lock){0}) // ??
#elif defined(__cplusplus) && __cplusplus >= 201103L
#define OS_UNFAIR_LOCK_INIT (os_unfair_lock{}) // ??
#elif defined(__cplusplus)
#define OS_UNFAIR_LOCK_INIT (os_unfair_lock()) // ??
#else
#define OS_UNFAIR_LOCK_INIT {0} // ??
#endif
#endif // OS_UNFAIR_LOCK_INIT
- os_unfair_lock_lock 加鎖。
/*!
* @function os_unfair_lock_lock
*
* @abstract
* Locks an os_unfair_lock. // 鎖定一個(gè) os_unfair_lock
*
* @param lock
* Pointer to an os_unfair_lock. // 參數(shù)是一個(gè) os_unfair_lock 指針
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_lock(os_unfair_lock_t lock);
-
os_unfair_lock_trylock
嘗試加鎖捏检。
/*!
* @function os_unfair_lock_trylock
*
* @abstract
* Locks an os_unfair_lock if it is not already locked.
* 鎖定一個(gè) os_unfair_lock荞驴,如果它是之前尚未鎖定的。
*
* @discussion
* It is invalid to surround this function with a retry loop, if this function
* returns false, the program must be able to proceed without having acquired
* the lock, or it must call os_unfair_lock_lock() directly (a retry loop around
* os_unfair_lock_trylock() amounts to an inefficient implementation of
* os_unfair_lock_lock() that hides the lock waiter from the system and prevents
* resolution of priority inversions).
* 如果此函數(shù)返回 false贯城,則用重試循環(huán)包圍此函數(shù)是無效的戴尸,程序必須能夠有能力處理這種沒有獲得鎖的情況保證程序正常運(yùn)行,
* 或者必須直接調(diào)用 os_unfair_lock_lock()(os_unfair_lock_lock 會(huì)使線程阻塞一直到獲得鎖為止)冤狡。
* (圍繞 os_unfair_lock_trylock() 的重試循環(huán)等于 os_unfair_lock_lock() 的低效實(shí)現(xiàn)孙蒙,
* 該實(shí)現(xiàn)將 lock waiter 從系統(tǒng)中隱藏并解決了優(yōu)先級(jí)反轉(zhuǎn)問題)
*
* @param lock
* Pointer to an os_unfair_lock.
* 參數(shù)是一個(gè)指向 os_unfair_lock 的指針。
*
* @result
* Returns true if the lock was succesfully locked and false if the lock was already locked.
* 鎖定成功返回 true悲雳,如果之前已經(jīng)被鎖定則返回 false挎峦。
*
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_WARN_RESULT OS_NONNULL_ALL
bool os_unfair_lock_trylock(os_unfair_lock_t lock);
-
os_unfair_lock_unlock
解鎖。
/*!
* @function os_unfair_lock_unlock
*
* @abstract
* Unlocks an os_unfair_lock. // 解鎖
*
* @param lock
* Pointer to an os_unfair_lock.
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_unlock(os_unfair_lock_t lock);
-
os_unfair_lock_assert_owner
判斷當(dāng)前線程是否是os_unfair_lock
的所有者合瓢,否則觸發(fā)斷言坦胶。
/*!
* @function os_unfair_lock_assert_owner
*
* @abstract
* Asserts that the calling thread is the current owner of the specified unfair lock.
*
* @discussion
* If the lock is currently owned by the calling thread, this function returns.
* 如果鎖當(dāng)前由調(diào)用線程所擁有,則此函數(shù)正常執(zhí)行返回。
*
* If the lock is unlocked or owned by a different thread, this function asserts and terminates the process.
* 如果鎖是未鎖定或者由另一個(gè)線程所擁有顿苇,則執(zhí)行斷言峭咒。
*
* @param lock
* Pointer to an os_unfair_lock.
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_assert_owner(os_unfair_lock_t lock);
-
os_unfair_lock_assert_not_owner
與上相反,如果當(dāng)前線程是指定os_unfair_lock
的所有者則觸發(fā)斷言纪岁。
/*!
* @function os_unfair_lock_assert_not_owner
*
* @abstract
* Asserts that the calling thread is not the current owner of the specified unfair lock.
*
* @discussion
* If the lock is unlocked or owned by a different thread, this function returns.
*
* If the lock is currently owned by the current thread, this function assertsand terminates the process.
*
* @param lock
* Pointer to an os_unfair_lock.
*/
OS_UNFAIR_LOCK_AVAILABILITY
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_assert_not_owner(os_unfair_lock_t lock);
- 測(cè)試
os_unfair_lock_assert_owner
和os_unfair_lock_assert_not_owner
凑队。
dispatch_async(globalQueue_DEFAULT, ^{
os_unfair_lock_assert_owner(&self->_unfairL);
});
os_unfair_lock_assert_not_owner(&self->_unfairL);
pthread_mutex_t
pthread_mutex_t
是 C 語言下多線程互斥鎖的方式,是跨平臺(tái)使用的鎖幔翰,等待鎖的線程會(huì)處于休眠狀態(tài)漩氨,可根據(jù)不同的屬性配置把pthread_mutex_t
初始化為不同類型的鎖,例如:互斥鎖遗增、遞歸鎖叫惊、條件鎖。當(dāng)使用遞歸鎖時(shí)做修,允許同一個(gè)線程重復(fù)進(jìn)行加鎖霍狰,另一個(gè)線程訪問時(shí)就會(huì)等待,這樣可以保證多線程時(shí)訪問共用資源的安全性饰及。pthread_mutex_t
使用時(shí)首先要引入頭文件 #import <pthread.h>
蔗坯。
PTHREAD_MUTEX_NORMAL // 缺省類型,也就是普通類型旋炒,當(dāng)一個(gè)線程加鎖后步悠,其余請(qǐng)求鎖的線程將形成一個(gè)隊(duì)列,并在解鎖后先進(jìn)先出原則獲得鎖瘫镇。
PTHREAD_MUTEX_ERRORCHECK // 檢錯(cuò)鎖鼎兽,如果同一個(gè)線程請(qǐng)求同一個(gè)鎖,則返回 EDEADLK铣除,否則與普通鎖類型動(dòng)作相同谚咬。這樣就保證當(dāng)不允許多次加鎖時(shí)不會(huì)出現(xiàn)嵌套情況下的死鎖
PTHREAD_MUTEX_RECURSIVE //遞歸鎖,允許同一個(gè)線程對(duì)同一鎖成功獲得多次尚粘,并通過多次 unlock 解鎖择卦。
PTHREAD_MUTEX_DEFAULT // 適應(yīng)鎖昼弟,動(dòng)作最簡單的鎖類型旦万,僅等待解鎖后重新競爭修己,沒有等待隊(duì)列锄弱。
pthread_mutex_trylock
和 trylock
不同,trylock
返回的是 YES
和 NO
啡邑,pthread_mutex_trylock
加鎖成功返回的是0
瞄崇,失敗返回的是錯(cuò)誤提示碼纹因。
pthread_mutex_t 簡單使用
pthread_mutex_t
初始化時(shí)使用不同的 pthread_mutexattr_t
可獲得不同類型的鎖盔腔。
互斥鎖( PTHREAD_MUTEX_DEFAULT 或 PTHREAD_MUTEX_NORMAL )
#import "ViewController.h"
#import <pthread.h> // pthread_mutex_t
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) pthread_mutex_t lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sum = 0;
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 1. 互斥鎖杠茬,默認(rèn)狀態(tài)為互斥鎖
// 初始化屬性
pthread_mutexattr_t att;
pthread_mutexattr_init(&att);
// 設(shè)置屬性月褥,描述鎖是什么類型
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_init(&self->_lock, &att);
// 銷毀屬性
pthread_mutexattr_destroy(&att);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_unlock(&self->_lock);
NSLog(@"?????? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_unlock(&self->_lock);
NSLog(@"?????? %ld", (long)self.sum);
});
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
// 銷毀鎖
pthread_mutex_destroy(&self->_lock);
}
@end
// 打印 ???:
?????? 10000
?????? 20000
??????????????? dealloc 同時(shí)釋放??...
遞歸鎖( PTHREAD_MUTEX_RECURSIVE )
#import "ViewController.h"
#import <pthread.h> // pthread_mutex_t
static int count = 3;
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, assign) pthread_mutex_t recursivelock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sum = 0;
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2. 遞歸鎖(PTHREAD_MUTEX_RECURSIVE)
pthread_mutexattr_t recursiveAtt;
pthread_mutexattr_init(&recursiveAtt);
// 設(shè)置屬性,描述鎖是什么類型
pthread_mutexattr_settype(&recursiveAtt, PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_init(&self->_recursivelock, &recursiveAtt);
// 銷毀屬性
pthread_mutexattr_destroy(&recursiveAtt);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_recursivelock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_unlock(&self->_recursivelock);
NSLog(@"?????? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 遞歸鎖驗(yàn)證
[self recursiveAction];
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_recursivelock);
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
pthread_mutex_lock(&self->_recursivelock);
NSLog(@"?????? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 遞歸鎖驗(yàn)證
[self recursiveAction];
});
}
#pragma mark - Private Methods
- (void)recursiveAction {
pthread_mutex_lock(&self->_recursivelock);
NSLog(@"?????? count = %d", count);
if (count > 0) {
count--;
[self recursiveAction];
}
// else { // 如果是單線程的話瓢喉,這里加一個(gè)遞歸出口沒有任何問題
// return;
// }
pthread_mutex_unlock(&self->_recursivelock);
count = 3;
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
pthread_mutex_destroy(&self->_recursivelock);
}
@end
// 打印 ???:
?????? 10000
?????? count = 3
?????? count = 2
?????? count = 1
?????? count = 0
?????? 20000
?????? count = 3
?????? count = 2
?????? count = 1
?????? count = 0
??????????????? dealloc 同時(shí)釋放??...
條件鎖
首先設(shè)定以下場景宁赤,兩條線程A
和B
,A
線程中執(zhí)行刪除數(shù)組元素栓票,B
線程中執(zhí)行添加數(shù)組元素决左,由于不知道哪個(gè)線程會(huì)先執(zhí)行,所以需要加鎖實(shí)現(xiàn)逗载,只有在添加之后才能執(zhí)行刪除操作哆窿,為互斥鎖添加條件可以實(shí)現(xiàn)链烈。通過此方法可以實(shí)現(xiàn)線程依賴厉斟。
#import "ViewController.h"
#import <pthread.h> // pthread_mutex_t
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, assign) pthread_cond_t condition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化數(shù)組
self.dataArr = [NSMutableArray array];
// 初始化鎖
pthread_mutexattr_t att;
pthread_mutexattr_init(&att);
pthread_mutexattr_settype(&att, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(&self->_lock, &att);
pthread_mutexattr_destroy(&att);
// 初始化條件
pthread_cond_init(&self->_condition, NULL);
dispatch_queue_t global_DEFAULT = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t global_HIGH = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
__weak typeof(self) _self = self;
dispatch_async(global_HIGH, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
NSLog(@"??????????????? delete begin");
if (self.dataArr.count < 1) {
pthread_cond_wait(&self->_condition, &self->_lock);
}
[self.dataArr removeLastObject];
NSLog(@"數(shù)組執(zhí)行刪除元素操作");
pthread_mutex_unlock(&self->_lock);
});
dispatch_async(global_DEFAULT, ^{
__strong typeof(_self) self = _self;
if (!self) return;
pthread_mutex_lock(&self->_lock);
NSLog(@"??????????????? add begin");
[self.dataArr addObject:@"CHM"];
pthread_cond_signal(&self->_condition);
NSLog(@"數(shù)組執(zhí)行添加元素操作");
pthread_mutex_unlock(&self->_lock);
});
NSThread *deThread = [[NSThread alloc] initWithTarget:self selector:@selector(deleteObj) object:nil];
[deThread start];
// sleep 1 秒,確保刪除元素的線程先獲得鎖
sleep(1);
NSThread *addThread = [[NSThread alloc] initWithTarget:self selector:@selector(addObj) object:nil];
[addThread start];
}
#pragma mark - Private Methods
- (void)deleteObj {
pthread_mutex_lock(&self->_lock);
NSLog(@"??????????????? delete begin");
// 添加判斷强衡,如果沒有數(shù)據(jù)則添加條件
if (self.dataArr.count < 1) {
// 添加條件擦秽,如果數(shù)組為空,則添加等待線程休眠漩勤,將鎖讓出感挥,這里會(huì)將鎖讓出去,所以下面的 addObj 線程才能獲得鎖
// 接收到信號(hào)時(shí)會(huì)再次加鎖越败,然后繼續(xù)向下執(zhí)行
pthread_cond_wait(&self->_condition, &self->_lock);
}
[self.dataArr removeLastObject];
NSLog(@"數(shù)組執(zhí)行刪除元素操作");
pthread_mutex_unlock(&self->_lock);
}
- (void)addObj {
pthread_mutex_lock(&self->_lock);
NSLog(@"??????????????? add begin");
[self.dataArr addObject:@"HTI"];
// 發(fā)送信號(hào)触幼,說明已經(jīng)添加元素了
pthread_cond_signal(&self->_condition);
NSLog(@"數(shù)組執(zhí)行添加元素操作");
pthread_mutex_unlock(&self->_lock);
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
pthread_mutex_destroy(&self->_lock);
pthread_cond_destroy(&self->_condition);
}
@end
// 打印 ???:
??????????????? delete begin
??????????????? add begin
數(shù)組執(zhí)行添加元素操作
數(shù)組執(zhí)行刪除元素操作
??????????????? dealloc 同時(shí)釋放??...
NSLock
繼承自 NSObject 并遵循 NSLocking 協(xié)議,lock 方法加鎖究飞,unlock 方法解鎖置谦,tryLock 嘗試并加鎖,如果返回 true 表示加鎖成功亿傅,返回 false 表示加鎖失敗媒峡,謹(jǐn)記返回的 BOOL 表示加鎖動(dòng)作的成功或失敗,并不是能不能加鎖葵擎,即使加鎖失敗也會(huì)不會(huì)阻塞當(dāng)前線程谅阿。lockBeforeDate: 是在指定的 Date 之前嘗試加鎖,如果在指定的時(shí)間之前都不能加鎖酬滤,則返回 NO签餐,且會(huì)阻塞當(dāng)前線程。大概可以使用在:先預(yù)估上一個(gè)臨界區(qū)的代碼執(zhí)行完畢需要多少時(shí)間盯串,然后在這個(gè)時(shí)間之后為另一個(gè)代碼段來加鎖氯檐。
1.基于 mutex 基本鎖的封裝,更加面向?qū)ο笞炱ⅲ却i的線程會(huì)處于休眠狀態(tài)男摧。
2.遵守 NSLocking 協(xié)議蔬墩,NSLocking 協(xié)議中僅有兩個(gè)方法 -(void)lock 和 -(void)unlock。
3.可能會(huì)用到的方法:
4.初始化跟其他 OC 對(duì)象一樣耗拓,直接進(jìn)行 alloc 和 init 操作拇颅。
5.-(void)lock; 加鎖。
6.-(void)unlock; 解鎖乔询。
7.-(BOOL)tryLock; 嘗試加鎖樟插。
8.-(BOOL)lockBeforeDate:(NSDate *)limit; 在某一個(gè)時(shí)間點(diǎn)之前等待加鎖。
9.在主線程連續(xù)調(diào)用 [self.lock lock] 會(huì)導(dǎo)致主線程死鎖竿刁。
10.在主線程沒有獲取 Lock 的情況下和在獲取 Lock 的情況下黄锤,連續(xù)兩次 [self.lock unlock] 都不會(huì)發(fā)生異常。(其他的鎖可能連續(xù)解鎖的情況下會(huì)導(dǎo)致 crash食拜,還沒有來的及測(cè)試)
11.在子線程連續(xù) [self.lock lock] 會(huì)導(dǎo)致死鎖鸵熟,同時(shí)別的子線獲取 self.lock 則會(huì)一直等待下去。
12.同時(shí)子線程死鎖會(huì)導(dǎo)致 ViewController 不釋放负甸。
NSLock 使用
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, strong) NSLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sum = 0;
self.lock = [[NSLock alloc] init];
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 如果此處加鎖失敗流强,則阻塞當(dāng)前線程,下面的代碼不會(huì)執(zhí)行呻待,
// 直到等到 lock 被其他線程釋放了打月,它可以加鎖了,才會(huì)接著執(zhí)行下面的代碼
[self.lock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.lock unlock];
NSLog(@"?????? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
// 如果此處加鎖失敗蚕捉,則阻塞當(dāng)前線程奏篙,下面的代碼不會(huì)執(zhí)行,
// 直到等到 lock 被其他線程釋放了迫淹,它可以加鎖了秘通,才會(huì)接著執(zhí)行下面的代碼
[self.lock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.lock unlock];
NSLog(@"?????? %ld", (long)self.sum);
});
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
}
@end
// 打印結(jié)果:
?????? 20000
?????? 10000
??????????????? dealloc 同時(shí)釋放??...
__weak typeof(self) _self = self;
// 線程 1
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self.lock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
sleep(3);
[self.lock unlock];
NSLog(@"?????? %ld", (long)self.sum);
});
// 線程 2
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
sleep(1); // 保證讓線程 1 先獲得鎖
// 如果此處用 1,則在這個(gè)時(shí)間點(diǎn)不能獲得鎖
// 如果是用大于 2 的數(shù)字千绪,則能獲得鎖
// 且這個(gè) if 函數(shù)是會(huì)阻塞當(dāng)前線程的
if ([self.lock lockBeforeDate: [NSDate dateWithTimeIntervalSinceNow:1]]) {
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.lock unlock];
} else {
NSLog(@"lockBeforeDate 失敗充易,會(huì)直接來到這里嗎,會(huì)不阻塞當(dāng)前線程嗎荸型?");
}
NSLog(@"?????? %ld", (long)self.sum);
});
[self.lock lockBeforeDate: [NSDate dateWithTimeIntervalSinceNow:1]]
盹靴,lockBeforeDate: 方法會(huì)在指定 Date 之前嘗試加鎖,且這個(gè)過程是會(huì)阻塞線程 2 的瑞妇,如果在指定時(shí)間之前都不能加鎖稿静,則返回 false,在指定時(shí)間之前能加鎖辕狰,則返回 true改备。
_priv 和 name,檢測(cè)各個(gè)階段蔓倍,_priv 一直是 NULL悬钳。name 是用來標(biāo)識(shí)的盐捷,用來輸出的時(shí)候作為 lock 的名稱。如果是三個(gè)線程默勾,那么一個(gè)線程在加鎖的時(shí)候碉渡,其余請(qǐng)求鎖的的線程將形成一個(gè)等待隊(duì)列,按先進(jìn)先出原則母剥,這個(gè)結(jié)果可以通過修改線程優(yōu)先級(jí)進(jìn)行測(cè)試得出滞诺。
NSRecursiveLock
NSRecursiveLock 是遞歸鎖,和 NSLock 的區(qū)別在于环疼,它可以在同一個(gè)線程中重復(fù)加鎖也不會(huì)導(dǎo)致死鎖习霹。NSRecursiveLock 會(huì)記錄加鎖和解鎖的次數(shù),當(dāng)二者次數(shù)相等時(shí)炫隶,此線程才會(huì)釋放鎖淋叶,其它線程才可以上鎖成功。
1.同 NSLock 一樣等限,也是基于 mutex 的封裝爸吮,不過是基于 mutex 遞歸鎖的封裝芬膝,所以這是一個(gè)遞歸鎖望门。
2.遵守 NSLocking 協(xié)議,NSLocking 協(xié)議中僅有兩個(gè)方法 -(void)lock 和 -(void)unlock锰霜。
3.可能會(huì)用到的方法:
4.繼承自 NSObject筹误,所以初始化跟其他 OC 對(duì)象一樣,直接進(jìn)行 alloc 和 init 操作癣缅。
5.-(void)lock; 加鎖
6.-(void)unlock; 解鎖
7.-(BOOL)tryLock; 嘗試加鎖
8-(BOOL)lockBeforeDate:(NSDate *)limit; 在某一個(gè)時(shí)間點(diǎn)之前等待加鎖厨剪。
9.遞歸鎖是可以在同一線程連續(xù)調(diào)用 lock 不會(huì)直接導(dǎo)致阻塞死鎖,但是依然要執(zhí)行相等次數(shù)的 unlock友存。不然異步線程再獲取該遞歸鎖會(huì)導(dǎo)致該異步線程阻塞死鎖祷膳。
10.遞歸鎖允許同一線程多次加鎖,不同線程進(jìn)入加鎖入口會(huì)處于等待狀態(tài)屡立,需要等待上一個(gè)線程解鎖完成才能進(jìn)入加鎖狀態(tài)直晨。
NSRecursiveLock 使用
其實(shí)是實(shí)現(xiàn)上面 pthread_mutex_t
和 PTHREAD_MUTEX_RECURSIVE
完成的遞歸鎖場景,只是這里使用 NSRecursiveLock API
更加精簡膨俐,使用起來更加簡單方便勇皇。
#import "ViewController.h"
static int count = 3;
@interface ViewController ()
@property (nonatomic, assign) NSInteger sum;
@property (nonatomic, strong) NSLock *lock;
@property (nonatomic, strong) NSRecursiveLock *recursiveLock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.sum = 0;
self.recursiveLock = [[NSRecursiveLock alloc] init];
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self.recursiveLock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.recursiveLock unlock];
NSLog(@"?????? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self recursiveAction];
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self.recursiveLock lock];
for (unsigned int i = 0; i < 10000; ++i) {
self.sum++;
}
[self.recursiveLock unlock];
NSLog(@"?????? %ld", (long)self.sum);
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self recursiveAction];
});
}
#pragma mark - Private Methods
- (void)recursiveAction {
[self.recursiveLock lock];
NSLog(@"?????? count = %d", count);
if (count > 0) {
count--;
[self recursiveAction];
}
// else { // 如果是單線程的話,這里加一個(gè)遞歸出口沒有任何問題
// return;
// }
[self.recursiveLock unlock];
count = 3;
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
}
@end
// 打印結(jié)果:
?????? count = 3
?????? 10000
?????? count = 2
?????? count = 1
?????? count = 0
?????? 20000
?????? count = 3
?????? count = 2
?????? count = 1
?????? count = 0
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursieveBlock)(int);
RecursiveBlock = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value: %d", value);
RecursiveBlock(value - 1);
}
[lock unlock];
};
RecursiveBlock(2);
});
如上示例焚刺,如果用 NSLock 的話敛摘,lock 先鎖上,但未執(zhí)行解鎖的時(shí)候乳愉,就會(huì)進(jìn)入遞歸的下一層兄淫,并再次請(qǐng)求上鎖屯远,阻塞了該線程,線程被阻塞了捕虽,自然后面的解鎖代碼就永遠(yuǎn)不會(huì)執(zhí)行氓润,而形成了死鎖。而 NSRecursiveLock 遞歸鎖就是為了解決這個(gè)問題薯鳍。
NSCondition
NSCondition 的對(duì)象實(shí)際上作為一個(gè)鎖和一個(gè)線程檢查器咖气,鎖上之后其它線程也能上鎖,而之后可以根據(jù)條件決定是否繼續(xù)運(yùn)行線程挖滤,即線程是否要進(jìn)入 waiting 狀態(tài)崩溪,經(jīng)測(cè)試,NSCondition 并不會(huì)像上文的那些鎖一樣斩松,先輪詢伶唯,而是直接進(jìn)入 waiting 狀態(tài),當(dāng)其它線程中的該鎖執(zhí)行 signal 或者 broadcast 方法時(shí)惧盹,線程被喚醒乳幸,繼續(xù)運(yùn)行之后的方法。也就是使用 NSCondition 的模型為: 1. 鎖定條件對(duì)象钧椰。2. 測(cè)試是否可以安全的履行接下來的任務(wù)粹断。如果布爾值為假,調(diào)用條件對(duì)象的 wait 或者 waitUntilDate: 方法來阻塞線程嫡霞。再從這些方法返回瓶埋,則轉(zhuǎn)到步驟 2 重新測(cè)試你的布爾值。(繼續(xù)等待信號(hào)和重新測(cè)試诊沪,直到可以安全的履行接下來的任務(wù)养筒,waitUntilDate: 方法有個(gè)等待時(shí)間限制,指定的時(shí)間到了端姚,則返回 NO晕粪,繼續(xù)運(yùn)行接下來的任務(wù)。而等待信號(hào)渐裸,既線程執(zhí)行 [lock signal] 發(fā)送的信號(hào)巫湘。其中 signal 和 broadcast 方法的區(qū)別在于,signal 只是一個(gè)信號(hào)量橄仆,只能喚醒一個(gè)等待的線程剩膘,想喚醒多個(gè)就得多次調(diào)用,而 broadcast 可以喚醒所有在等待的線程盆顾,如果沒有等待的線程怠褐,這兩個(gè)方法都沒有作用。)
1.基于 mutex
基礎(chǔ)鎖和 cont
條件的封裝您宪,所以它是互斥鎖且自帶條件奈懒,等待鎖的線程休眠奠涌。
2.遵守NSLocking
協(xié)議,NSLocking
協(xié)議中僅有兩個(gè)方法 -(void)lock
和-(void)unlock
磷杏。
3.可能會(huì)用到的方法
4.初始化跟其它 OC 對(duì)象一樣溜畅,直接進(jìn)行alloc
和 init
操作。
5.-(void)lock;
加鎖
6.-(void)unlock;
解鎖
7.-(BOOL)tryLock;
嘗試加鎖
8.-(BOOL)lockBeforeDate:(NSDate *)limit;
在某一個(gè)時(shí)間點(diǎn)之前等待加鎖
9.-(void)wait;
等待條件(進(jìn)入休眠的同時(shí)放開鎖极祸,被喚醒的同時(shí)再次加鎖)
10.-(void)signal;
發(fā)送信號(hào)激活等待該條件的線程慈格,切記線程收到后是從 wait 狀態(tài)開始的
11.- (void)broadcast;
發(fā)送廣播信號(hào)激活等待該條件的所有線程,切記線程收到后是從 wait 狀態(tài)開始的
NSCondition 使用
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *dataArr;
@property (nonatomic, strong) NSCondition *condition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 初始化數(shù)組
self.dataArr = [NSMutableArray array];
self.condition = [[NSCondition alloc] init];
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) _self = self;
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self deleteObj];
});
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self deleteObj];
});
// sleep 0.5 秒遥金,確保刪除元素的操作先取得鎖
sleep(0.5);
dispatch_async(global_queue, ^{
__strong typeof(_self) self = _self;
if (!self) return;
[self addObj];
});
}
#pragma mark - Private Methods
- (void)deleteObj {
[self.condition lock];
NSLog(@"??????????????? delete begin");
// 添加判斷浴捆,如果沒有數(shù)據(jù)則添加條件
if (self.dataArr.count < 1) {
// 添加條件,如果數(shù)組為空稿械,則添加等待線程休眠选泻,將鎖讓出,這里會(huì)將鎖讓出去美莫,所以下面的 addObj 線程才能獲得鎖
// 接收到信號(hào)時(shí)會(huì)再次加鎖页眯,然后繼續(xù)向下執(zhí)行
NSLog(@"下面是進(jìn)入 wait...");
[self.condition wait];
// 當(dāng) broadcast 過來的時(shí)候還是繼續(xù)往下執(zhí)行,
// 切記不是從 deleteObj 函數(shù)頭部開始的厢呵,是從這里開始的
// 所以當(dāng)?shù)谝粋€(gè)異步刪除數(shù)組元素后窝撵,第二個(gè)異步進(jìn)來時(shí)數(shù)組已經(jīng)空了
NSLog(@"接收到 broadcast 或 signal 后的函數(shù)起點(diǎn)");
}
NSLog(@"%@", self.dataArr);
[self.dataArr removeLastObject];
NSLog(@"??????????????? 數(shù)組執(zhí)行刪除元素操作");
[self.condition unlock];
}
- (void)addObj {
[self.condition lock];
NSLog(@"??????????????? add begin");
[self.dataArr addObject:@"CHM"];
// 發(fā)送信號(hào),說明已經(jīng)添加元素了
// [self.condition signal];
// 通知所有符合條件的線程
[self.condition broadcast];
NSLog(@"??????????????? 數(shù)組執(zhí)行添加元素操作");
[self.condition unlock];
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
}
@end
// 打印結(jié)果:
// 這里兩個(gè)異步線程執(zhí)行都 [self.condition lock]述吸,都正常進(jìn)入了忿族,
// 并沒有因?yàn)?self.condition 先被一條線程獲取加鎖了而另一條線程處于阻塞等待狀態(tài),
??????????????? delete begin
下面是進(jìn)入 wait...
??????????????? delete begin
下面是進(jìn)入 wait...
??????????????? add begin
??????????????? 數(shù)組執(zhí)行添加元素操作
接收到 broadcast 或 signal 后的函數(shù)起點(diǎn)
(
CHM
)
??????????????? 數(shù)組執(zhí)行刪除元素操作
接收到 broadcast 或 signal 后的函數(shù)起點(diǎn)
(
)
??????????????? 數(shù)組執(zhí)行刪除元素操作
??????????????? dealloc 同時(shí)釋放??...
NSConditionLock
NSConditionLock 和 NSLock 類似蝌矛,同樣是繼承自 NSObject 和遵循 NSLocking 協(xié)議,加解鎖 try 等方法都類似错英,只是多了一個(gè) condition 屬性入撒,以及每個(gè)操作都多了一個(gè)關(guān)于 condition 屬性的方法,例如 tryLock 和 tryLockWhenCondition:椭岩,NSConditionLock 可以稱為條件鎖茅逮。只有 condition 參數(shù)與初始化的時(shí)候相等或者上次解鎖后設(shè)置的 condition 相等,lock 才能正確的進(jìn)行加鎖操作判哥。unlockWithCondition: 并不是當(dāng) condition 符合條件時(shí)才解鎖献雅,而是解鎖之后,修改 condition 的值為入?yún)⑺疲?dāng)使用 unlock 解鎖時(shí)挺身, condition 的值保持不變。如果初始化用 init锌仅,則 condition 默認(rèn)值為 0章钾。lockWhenCondition: 和 lock 方法類似墙贱,加鎖失敗會(huì)阻塞當(dāng)前線程,一直等下去贱傀,直到能加鎖成功惨撇。tryLockWhenCondition: 和 tryLock 類似,表示嘗試加鎖府寒,即使加鎖失敗也不會(huì)阻塞當(dāng)前線程魁衙,但是同時(shí)滿足 lock 是空閑狀態(tài)并且 condition 符合條件才能嘗試加鎖成功。從上面看出株搔,NSConditionLock 還可以實(shí)現(xiàn)任務(wù)之間的依賴纺棺。
1.基于 NSCondition
的進(jìn)一步封裝,可以更加高級(jí)的設(shè)置條件值邪狞。
假設(shè)有這樣的場景祷蝌,三個(gè)線程 A B C,執(zhí)行完 A 線程后才能執(zhí)行 B帆卓,執(zhí)行完 B 線程后執(zhí)行 C巨朦,就是為線程之間的執(zhí)行添加依賴,NSConditionLock 可以方便的完成這個(gè)功能剑令。
2.遵守 NSLocking 協(xié)議糊啡,NSLocking 協(xié)議中僅有兩個(gè)方法 -(void)lock 和 -(void)unlock。
3.可能用到的方法:
4.初始化跟其他 OC 對(duì)象一樣吁津,直接 alloc 和 initWithCondition:(NSInteger)condition 操作棚蓄;(如果使用 init 方法,則 condition 默認(rèn)為 0)碍脏。
5.有一個(gè)屬性是 @property(readonly) NSInteger condition; 用來設(shè)置條件值梭依,如果不設(shè)定,則默認(rèn)為零典尾。
6.-(void)lock; 直接加鎖役拴。
7.-(void)lockWhenCondition:(NSInteger)condition; 根據(jù) condition 值加鎖,如果入?yún)⒑彤?dāng)前的 condition 不等則不加钾埂。
8.-(void)unlockWithCondition:(NSInteger)condition; 解鎖, 并設(shè)定 condition 的值為入?yún)ⅰ?/p>
9.-(BOOL)tryLock; 嘗試加鎖河闰。
10.-(BOOL)lockBeforeDate:(NSDate *)limit; 在某一個(gè)時(shí)間點(diǎn)之前等待加鎖。
NSConditionLock 使用
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSConditionLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSConditionLock alloc] initWithCondition:0];
[self createThreads];
}
#pragma mark - Private Methods
- (void)createThreads {
// 需要執(zhí)行的順序?yàn)?A-B-C褥紫,但是因?yàn)樵谧泳€程中所以我們不能確定誰先執(zhí)行姜性,添加 sleep 使問題更突出點(diǎn),c 線程先啟動(dòng)然后是 b 然后是 a髓考。
NSThread *c = [[NSThread alloc] initWithTarget:self selector:@selector(threadC) object:nil];
[c start];
sleep(0.2);
NSThread *b = [[NSThread alloc] initWithTarget:self selector:@selector(threadB) object:nil];
[b start];
sleep(0.2);
NSThread *a = [[NSThread alloc] initWithTarget:self selector:@selector(threadA) object:nil];
[a start];
}
- (void)threadA {
NSLog(@"A begin");
[self.lock lockWhenCondition:0]; // 此時(shí) Condition 值為 0 才能加鎖成功部念,因?yàn)?Condition 初始值是 0,所以只有 A 能加鎖成功
NSLog(@"A threadExcute");
[self.lock unlockWithCondition:1]; // 解鎖并把 Condition 設(shè)置為 1
// [self unlock]; // 如果此處使用 unlock,則導(dǎo)致 B C 線程死鎖印机,且導(dǎo)致 ViewController 不釋放
}
- (void)threadB {
NSLog(@"B begin");
[self.lock lockWhenCondition:1]; // 此時(shí) Condition 值為 1 才能加鎖成功
NSLog(@"B threadExcute");
[self.lock unlockWithCondition:2]; // 解鎖并把 Condition 設(shè)置為 2
}
- (void)threadC {
NSLog(@"C begin");
[self.lock lockWhenCondition:2]; // 此時(shí) Condition 值為 2 才能加鎖成功
NSLog(@"C threadExcute");
[self.lock unlock]; // 解鎖
}
#pragma mark - dealloc
- (void)dealloc {
NSLog(@"??????????????? dealloc 同時(shí)釋放??...");
}
// 打印結(jié)果:
// 雖然啟動(dòng)順序是 C B A矢腻,但是執(zhí)行順序是 A B C,正是由 Condition 條件控制的射赛,只有 Condition 匹配才能加鎖成功多柑,否則一直阻塞等待
C begin
B begin
A begin
A threadExcute
B threadExcute
C threadExcute
??????????????? dealloc 同時(shí)釋放??...
1.[self.lock unlock];
執(zhí)行后 condition
保持不變,依然是初始化的值或者是上次執(zhí)行 lockWhenCondition:
時(shí)的值楣责。
2.A B C 3 條線程必須都執(zhí)行加鎖和解鎖后 ViewController
才能正常釋放竣灌,除了最后一條線程可以直接使用 unlock
執(zhí)行解鎖外,前兩條線程 unlockWithCondition:
的入?yún)?code>condition 的值必須和 NSConditionLock
當(dāng)前的 condition
的值匹配起來秆麸。保證每條線程都lock
和unlock
初嘹,無法正常執(zhí)行時(shí)都會(huì)導(dǎo)致線程阻塞等待,ViewController
不會(huì)釋放沮趣。
3.在同一線程連續(xù)[self.lock lockWhenCondition:1];
會(huì)直接阻塞死鎖屯烦,不管用的condition
是否和當(dāng)前鎖的 condition
相等,都會(huì)導(dǎo)致阻塞死鎖房铭。
NSLocking驻龟、NSLock、NSConditionLock缸匪、NSRecursiveLock翁狐、NSCondition 定義
#import <Foundation/NSObject.h>
@class NSDate;
NS_ASSUME_NONNULL_BEGIN
// NSLocking 協(xié)議,上面提到鎖的類型只要是 NS 開頭的都會(huì)遵守此協(xié)議
@protocol NSLocking // 看到 NSLocking 協(xié)議只有加鎖和解鎖兩個(gè)協(xié)議方法
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> { // NSLock 是繼承自 NSObject 并遵守 NSLocking 協(xié)議
@private
void *_priv;
}
- (BOOL)tryLock; // 嘗試加鎖凌蔬,返回 true 表示加鎖成功
- (BOOL)lockBeforeDate:(NSDate *)limit; // 在某個(gè) NSDate 之前加鎖
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
// 條件鎖
@interface NSConditionLock : NSObject <NSLocking> { // 繼承自 NSObject 并遵守 NSLocking 協(xié)議
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition; // 只讀的 condition 屬性
- (void)lockWhenCondition:(NSInteger)condition; // 根據(jù) condition 值加鎖, 如果值不滿足, 則不加;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition; // 解鎖, 并設(shè)定 condition 的值;
- (BOOL)lockBeforeDate:(NSDate *)limit; // 在某一個(gè)時(shí)間點(diǎn)之前等待加鎖
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
// 遞歸鎖
@interface NSRecursiveLock : NSObject <NSLocking> { // 繼承自 NSObject 并遵守 NSLocking 協(xié)議
@private
void *_priv;
}
- (BOOL)tryLock; // 嘗試加鎖露懒,返回 true 表示加鎖成功
- (BOOL)lockBeforeDate:(NSDate *)limit; // 在某個(gè) NSDate 之前加鎖
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0))
@interface NSCondition : NSObject <NSLocking> { // 繼承自 NSObject 并遵守 NSLocking 協(xié)議
@private
void *_priv;
}
- (void)wait; // 添加等待,線程休眠砂心,并將鎖讓出
- (BOOL)waitUntilDate:(NSDate *)limit; // 某個(gè) NSDate
- (void)signal; // 發(fā)送信號(hào)懈词,告知等待的線程,條件滿足了
- (void)broadcast; // 通知所有符合條件的線程计贰,(通知所有在等待的線程)
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
NS_ASSUME_NONNULL_END