dispatch_once
dispatch_once是我們都所熟知的,它的用途就像名字一樣,執(zhí)行某個事件一次且僅只有一次。
它只有兩個參數(shù),第一個參數(shù)是once,第二個是首次執(zhí)行的block塊,調(diào)用起來是這樣的
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
// some one-time task
});
這個API用于全局的dictionary、單例、緩存,或者其他的只需要執(zhí)行一次的任務(wù)。在一個單線程中,這種調(diào)用可能意義并不是很大,可以用一個簡單的if語句來代替,但是我們的應(yīng)用程序往往都是多線程的,dispatch_once是線程安全的,它可以保證在多個線程同時調(diào)用的時候,dispatch_once只會執(zhí)行一次,并且所有的線程在dispatch_once返回之前都會進入等待狀態(tài)。雖然我們自己來實現(xiàn)并不是難事,但是dispatch_once的速度是非常的快的,效率很高
dispatch_once在單線程的實現(xiàn)
讓我們先來看看dispatch_once在單線程中是怎么實現(xiàn)的,這在實際開發(fā)中是意義并不大,但是讓我們對它的理解有更好的幫助。注意,dispatch_once是由一個long型typedef來進行控制的,初始化為0,這里是它的實現(xiàn):
void SimpleOnce(dispatch_once_t *predicate, dispatch_block_t block) {
if(!*predicate) {
block(); *predicate = 1;
}
}
它的實現(xiàn)非常的簡單,如果predicate為0,block進行回調(diào),并把predicate設(shè)置為1。在后續(xù)的執(zhí)行中將不會再調(diào)用block,這正是我們想要的,但是這在多線程下是完全不安全的,兩個線程可以同時執(zhí)行到if語句,會多次調(diào)用到block,這種情況就非常糟糕,并不是我們想要的結(jié)果。
在平時開發(fā)的時候這是常有的情況,那么我們就得考慮到線程安全,往往線程安全的代碼會給性能帶來隱患。
性能問題
當(dāng)我們談到dispatch_once的性能的時候,我們實際上從三種不同的方面來進行考慮
- 當(dāng)?shù)谝淮螆?zhí)行
dispatch_once,傳入predicate參數(shù),由哪個線程來執(zhí)行block - 在首次調(diào)用
dispatch_once之后,在block執(zhí)行完畢之前,調(diào)用者不得不等待block執(zhí)行完畢再繼續(xù)執(zhí)行 - 在
dispatch_once首次執(zhí)行完成之后并且block也執(zhí)行完成了,是無需等待的,是可以立即繼續(xù)執(zhí)行的
第一條在很大程度上是不重要的,畢竟它只會發(fā)生一次。
第二條的性能問題也是同樣重要的,它可能潛在的發(fā)生幾次,但是它只是在block正在執(zhí)行的時候才會發(fā)生。大多數(shù)情況下,是永遠不會發(fā)生的,也可能只會發(fā)生一次,即使在強度測試下,有許多線程同時調(diào)用dispatch_once,那么block將會在執(zhí)行上耗時,這些調(diào)用都會等待block執(zhí)行完成,所以就消耗了一些不必要的CPU
第三條性能問題是及其重要的。發(fā)生這種情況可能會成百上千次,我們希望dispatch_once用于保護一次性執(zhí)行一個計算,這個計算結(jié)果是在任何地方都可以使用的。在理想情況下,堅持用dispatch_once去計算,計算成本應(yīng)該提前于明確的計算時間,計算結(jié)果會賦值在一個全局變量上,我們希望這兩塊代碼有相同的執(zhí)行:
id gObject;
void Compute(void) {
gObject = ...;
}
id Fetch(void) {
return gObject;
}
id DispatchFetch(void) {
static id object;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
object = ...;
});
return object;
}
鎖(Locks)
使線程安全的標(biāo)準(zhǔn)方法是用鎖包圍訪問共享數(shù)據(jù)的區(qū)域,這是一個很你難的問題,因為沒有一個很適合的地方放置這個鎖來保護臨近的數(shù)據(jù)
我們可以通過clang的命令看看它的實現(xiàn)原理,可以看到一個包含鎖的struct