使用 objc_sync 進(jìn)行原子操作

什么叫原子操作

對(duì)于一個(gè)資源横朋,在寫入或讀取時(shí),只允許在一個(gè)時(shí)刻一個(gè)角色進(jìn)行操作兑燥,則為原子操作亮瓷。

你可以簡單粗暴地這么理解,我的銀行帳號(hào)里面有100塊錢降瞳,假如兩個(gè)人同時(shí)在不同的ATM機(jī)上操作嘱支,他們的操作都是取100塊錢,那ATM會(huì)不會(huì)都吐出100塊錢出來呢挣饥?

假如是除师,那么,取錢這個(gè)操作就是非原子性的扔枫。
假如不是汛聚,那么,取錢這個(gè)操作就是原子性的短荐。

Swift

對(duì)于 let 聲明的資源倚舀,永遠(yuǎn)是原子性的。
對(duì)于 var 聲明的資源搓侄,是非原子性的瞄桨,對(duì)其進(jìn)行讀寫時(shí),必須使用一定的手段讶踪,確保其值的正確性芯侥。

那么,在什么情況下,需要用這些手段去保證原子操作柱查。

  • 如果你對(duì)資源的一致性要求不高廓俭,則不需要
  • 如果資源可能在多個(gè)線程中被讀取或者寫入
  • 如果資源的訪問或?qū)懭胄枰挥行虻貓?zhí)行

一個(gè)栗子

假設(shè)我們需要一個(gè) ID 發(fā)生器,它的發(fā)生機(jī)制是從 0 ~ Int.max唉工,它生成的 ID 不能是重復(fù)的研乒,它生成的ID必須是正序的。

我們在 Playground 下輸入以下代碼淋硝,并查看結(jié)果雹熬。

import Foundation

struct TaskIDGenerater {
    
    static var value: Int = 0
    
    static func generate() -> Int {
        TaskIDGenerater.value++
        return TaskIDGenerater.value
    }
    
}

for _ in 0...10 {
    print(TaskIDGenerater.generate())
}

NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 5))

可以看到,控制臺(tái)輸出

1
2
3
4
5
6
7
8
9
10
11

現(xiàn)在谣膳,我們分開三個(gè)線程進(jìn)行 ID 生成操作竿报。

import Foundation

struct TaskIDGenerater {
    
    static var value: Int = 0
    
    static func generate() -> Int {
        TaskIDGenerater.value++
        return TaskIDGenerater.value
    }
    
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
    for _ in 0...10 {
        print(TaskIDGenerater.generate())
    }
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
    for _ in 0...10 {
        print(TaskIDGenerater.generate())
    }
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
    for _ in 0...10 {
        print(TaskIDGenerater.generate())
    }
}

NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 5))

現(xiàn)在,輸出變得奇怪無比了继谚!

2
2
2
5
5
5
8
8
8
11
11
11
14
14
14
17
17
17
19
20
20
23
22
23
26
26
26
29
29
29
31
32
32

解決方案

在 Objective-C 中烈菌, 我們可以使用以下方法解決問題。

@synchronized(<#token#>) {
    <#statements#>
}

在 Swift 中花履,我們使用類似的方法芽世。

objc_sync_enter(lock)
TaskIDGenerater.value++
objc_sync_exit(lock)

使用 objc_sync_enter 和 objc_sync_exit 包裹的代碼會(huì)被有序、同步地執(zhí)行诡壁。
同時(shí)济瓢,你需要給這兩個(gè)方法指定一個(gè)參考變量,這個(gè)參考變量可以是任意 var 類型的值妹卿。

但是葬荷,需要謹(jǐn)記,一旦 sync_enter 以后纽帖,整個(gè)應(yīng)用就會(huì)被鎖定,直至 sync_exit举反。
所以懊直,在 lock 的代碼區(qū)域中,要多加留意火鼻,看是否存在死鎖的現(xiàn)象室囊。

修改后的代碼如下

import Foundation

struct TaskIDGenerater {
    
    static var value: Int = 0
    
    static var lock: Int = 0
    
    static func generate() -> Int {
        objc_sync_enter(lock)
        TaskIDGenerater.value++
        let value = TaskIDGenerater.value
        objc_sync_exit(lock)
        return value
    }
    
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
    for _ in 0...10 {
        print(TaskIDGenerater.generate())
    }
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
    for _ in 0...10 {
        print(TaskIDGenerater.generate())
    }
}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
    for _ in 0...10 {
        print(TaskIDGenerater.generate())
    }
}

NSRunLoop.currentRunLoop().runUntilDate(NSDate(timeIntervalSinceNow: 5))

輸出

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

結(jié)語

在 Objective-C 的時(shí)代,我們使用 Copy() 以及非可變類型保證線程安全魁索。
在 Swift 的世界融撞,因?yàn)閘et的存在,可變性的線程安全問題經(jīng)常被開發(fā)者忽略粗蔚,如果哪一天尝偎,你的應(yīng)用出現(xiàn)了難以重現(xiàn)的問題,不妨從原子操作問題查起。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末致扯,一起剝皮案震驚了整個(gè)濱河市肤寝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抖僵,老刑警劉巖鲤看,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異耍群,居然都是意外死亡义桂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蹈垢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慷吊,“玉大人,你說我怎么就攤上這事耘婚“战剑” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵沐祷,是天一觀的道長嚷闭。 經(jīng)常有香客問我,道長赖临,這世上最難降的妖魔是什么胞锰? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮兢榨,結(jié)果婚禮上嗅榕,老公的妹妹穿的比我還像新娘。我一直安慰自己吵聪,他們只是感情好凌那,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吟逝,像睡著了一般帽蝶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上块攒,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天励稳,我揣著相機(jī)與錄音,去河邊找鬼囱井。 笑死驹尼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庞呕。 我是一名探鬼主播新翎,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了料祠?” 一聲冷哼從身側(cè)響起骆捧,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎髓绽,沒想到半個(gè)月后敛苇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡顺呕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年枫攀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片株茶。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡来涨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出启盛,到底是詐尸還是另有隱情蹦掐,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布僵闯,位于F島的核電站卧抗,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鳖粟。R本人自食惡果不足惜社裆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望向图。 院中可真熱鬧泳秀,春花似錦、人聲如沸榄攀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽檩赢。三九已至磺陡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間漠畜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工坞靶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憔狞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓彰阴,卻偏偏與公主長得像瘾敢,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫簇抵、插件庆杜、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,121評(píng)論 4 61
  • 嗯哼嗯哼蹦擦擦~~~ 轉(zhuǎn)載自:https://github.com/Tim9Liu9/TimLiu-iOS 目錄 ...
    philiha閱讀 4,910評(píng)論 0 6
  • 也不記得具體是哪一年了大學(xué)一位導(dǎo)師說過一句刻骨銘心的話人一旦降低了自己生活品格,就永回不到以前那個(gè)自我戀愛是開心的...
    項(xiàng)少少龍閱讀 142評(píng)論 0 0
  • 上晚班碟摆,感覺累晃财。
    阿3哥閱讀 186評(píng)論 1 0
  • 企業(yè)邀請(qǐng)函!5渫伞断盛!本月15號(hào)中進(jìn)云交易交易所在江西南昌市金燕國際溫泉大酒店!舉辦招商大會(huì)S涮颉钢猛!到場的有省長、發(fā)改委還有...
    藍(lán)瘦香菇棒棒噠閱讀 179評(píng)論 1 0