Swift多線程編程總結(jié)

在開(kāi)始多線程之前好港,我們先來(lái)了解幾個(gè)比較容易混淆的概念愉镰。

概念

線程與進(jìn)程

線程與進(jìn)程之間的關(guān)系,拿公司舉例钧汹,進(jìn)程相當(dāng)于部門丈探,線程相當(dāng)于部門職員。即進(jìn)程內(nèi)可以有一個(gè)或多個(gè)線程崭孤。

并發(fā)和并行

并發(fā)指的是多個(gè)任務(wù)交替占用CPU类嗤,并行指的是多個(gè)CPU同時(shí)執(zhí)行多個(gè)任務(wù)糊肠。好比火車站買票,并發(fā)指的是一個(gè)窗口有多人排隊(duì)買票遗锣,而并行指的是多個(gè)窗口有多人排隊(duì)買票货裹。

同步和異步

同步指在執(zhí)行一個(gè)函數(shù)時(shí),如果這個(gè)函數(shù)沒(méi)有執(zhí)行完畢精偿,那么下一個(gè)函數(shù)便不能執(zhí)行弧圆。異步指在執(zhí)行一個(gè)函數(shù)時(shí),不必等到這個(gè)函數(shù)執(zhí)行完畢笔咽,便可開(kāi)始執(zhí)行下一個(gè)函數(shù)搔预。

GCD

Swift3之后,GCD的Api有很大的調(diào)整叶组,從原來(lái)的C語(yǔ)言風(fēng)格的函數(shù)調(diào)用拯田,變?yōu)槊嫦驅(qū)ο蟮姆庋b,使用起來(lái)更加舒服甩十,靈活性更高船庇。

同步

let queue = DispatchQueue(label: "com.ffib.blog")

queue.sync {
    for i in 0..<5 {
        print(i)
    }
}

for i in 10..<15 {
    print(i)
}

output: 
0
1
2
3
4
10
11
12
13
14
復(fù)制代碼

從結(jié)果可以看出隊(duì)列同步操作時(shí),當(dāng)程序在進(jìn)行隊(duì)列任務(wù)時(shí)侣监,主線程的操作并不會(huì)被執(zhí)行鸭轮,這是由于當(dāng)程序在執(zhí)行同步操作時(shí),會(huì)阻塞線程橄霉,所以需要等待隊(duì)列任務(wù)執(zhí)行完畢窃爷,程序才可以繼續(xù)執(zhí)行。

異步

let queue = DispatchQueue(label: "com.ffib.blog")

queue.async {
    for i in 0..<5 {
        print(i)
    }
}

for i in 10..<15 {
    print(i)
}

output:
10
0
11
1
12
2
13
3
14
4
復(fù)制代碼

從結(jié)果可以看出隊(duì)列異步操作時(shí)姓蜂,當(dāng)程序在執(zhí)行隊(duì)列任務(wù)時(shí)按厘,不必等待隊(duì)列任務(wù)開(kāi)始執(zhí)行,便可執(zhí)行主線程的操作覆糟。與同步執(zhí)行相比刻剥,異步隊(duì)列并不會(huì)阻塞主線程,當(dāng)主線程空閑時(shí)滩字,便可執(zhí)行別的任務(wù)造虏。

QoS 優(yōu)先級(jí)

在實(shí)際開(kāi)發(fā)中,我們需要對(duì)任務(wù)分類麦箍,比如UI的顯示和交互操作等漓藕,屬于優(yōu)先級(jí)比較高的,有些不著急操作的挟裂,比如緩存操作享钞、用戶習(xí)慣收集等,相對(duì)來(lái)說(shuō)優(yōu)先級(jí)比較低诀蓉。
在GCD中栗竖,我們使用隊(duì)列和優(yōu)先級(jí)劃分任務(wù)暑脆,以達(dá)到更好的用戶體驗(yàn),選擇合適的優(yōu)先級(jí)狐肢,可以更好的分配CPU的資源添吗。
GCD內(nèi)采用DispatchQoS結(jié)構(gòu)體,如果沒(méi)有指定QoS份名,會(huì)使用default碟联。 以下等級(jí)由高到低。

public struct DispatchQoS : Equatable {

     public static let userInteractive: DispatchQoS //用戶交互級(jí)別僵腺,需要在極快時(shí)間內(nèi)完成的鲤孵,例如UI的顯示

     public static let userInitiated: DispatchQoS  //用戶發(fā)起,需要在很快時(shí)間內(nèi)完成的辰如,例如用戶的點(diǎn)擊事件普监、以及用戶的手勢(shì)
     。
     public static let `default`: DispatchQoS  //系統(tǒng)默認(rèn)的優(yōu)先級(jí)丧没,

     public static let utility: DispatchQoS   //實(shí)用級(jí)別鹰椒,不需要很快完成的任務(wù)

     public static let background: DispatchQoS  //用戶無(wú)法感知锡移,比較耗時(shí)的一些操作

     public static let unspecified: DispatchQoS
}

復(fù)制代碼

以下通過(guò)兩個(gè)例子來(lái)具體看一下優(yōu)先級(jí)的使用呕童。

相同優(yōu)先級(jí)

let queue1 = DispatchQueue(label: "com.ffib.blog.queue1", qos: .utility)
let queue2 = DispatchQueue(label: "com.ffib.blog.queue2", qos: .utility)

queue1.async {
    for i in 5..<10 {
        print(i)
    }
}

queue2.async {
    for i in 0..<5 {
        print(i)
    }
}
 output:
 0
 5
 1
 6
 2
 7
 3
 8
 4
 9
復(fù)制代碼

從結(jié)果可見(jiàn),優(yōu)先級(jí)相同時(shí)淆珊,兩個(gè)隊(duì)列是交替執(zhí)行的夺饲。

不同優(yōu)先級(jí)

let queue1 = DispatchQueue(label: "com.ffib.blog.queue1", qos: .default)
let queue2 = DispatchQueue(label: "com.ffib.blog.queue2", qos: .utility)

queue1.async {
    for i in 0..<5 {
        print(i)
    }
}

queue2.async {
    for i in 5..<10 {
        print(i)
    }
}

output:
0
5
1
2
3
4
6
7
8
9
復(fù)制代碼

從結(jié)果可見(jiàn),交替輸出施符,CPU會(huì)把更多的資源優(yōu)先分配給優(yōu)先級(jí)高的隊(duì)列往声,等到CPU空閑之后才會(huì)分配資源給優(yōu)先級(jí)低的隊(duì)列。

主隊(duì)列默認(rèn)使用擁有最高優(yōu)先級(jí)戳吝,即userInteractive浩销,所以慎用這一優(yōu)先級(jí),否則極有可能會(huì)影響用戶體驗(yàn)听哭。
一些不需要用戶感知的操作慢洋,例如緩存等,使用utility即可

串行隊(duì)列

在創(chuàng)建隊(duì)列時(shí)陆盘,不指定隊(duì)列類型時(shí)普筹,默認(rèn)為串行隊(duì)列。

let queue = DispatchQueue(label: "com.ffib.blog.initiallyInactive.queue", qos: .utility)

queue.async {
    for i in 0..<5 {
        print(i)
    }
}

queue.async {
    for i in 5..<10 {
        print(i)
    }
}

queue.async {
    for i in 10..<15 {
        print(i)
    }
}
output: 
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
復(fù)制代碼

從結(jié)果可見(jiàn)隊(duì)列執(zhí)行結(jié)果隘马,是按任務(wù)添加的順序太防,依次執(zhí)行。

并行隊(duì)列

let queue = DispatchQueue(label: "com.ffib.blog.concurrent.queue", qos: .utility, attributes: .concurrent)

queue.async {
    for i in 0..<5 {
        print(i)
    }
}

queue.async {
    for i in 5..<10 {
        print(i)
    }
}

queue.async {
    for i in 10..<15 {
        print(i)
    }
}
output:
5
0
10
1
2
3
11
4
6
12
7
13
8
14
9

復(fù)制代碼

從結(jié)果可見(jiàn)酸员,所有任務(wù)是以并行的狀態(tài)執(zhí)行的蜒车。另外在設(shè)置attributes參數(shù)時(shí)讳嘱,參數(shù)還有另一個(gè)枚舉值initiallyInactive,表示的任務(wù)不會(huì)自動(dòng)執(zhí)行酿愧,需要程序員去手動(dòng)觸發(fā)呢燥。如果不設(shè)置,默認(rèn)是添加完任務(wù)后寓娩,自動(dòng)執(zhí)行叛氨。


let queue = DispatchQueue(label: "com.ffib.blog.concurrent.queue", qos: .utility,
attributes: .initiallyInactive)
queue.async {
    for i in 0..<5 {
        print(i)
    }
}
queue.async {
    for i in 5..<10 {
        print(i)
    }
}
queue.async {
    for i in 10..<15 {
        print(i)
    }
}

//需要調(diào)用activate,激活隊(duì)列棘伴。
queue.activate()

output:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
復(fù)制代碼

從結(jié)果可見(jiàn)寞埠,只是把自動(dòng)執(zhí)行變?yōu)槭謩?dòng)觸發(fā),執(zhí)行結(jié)果沒(méi)變焊夸,添加這一屬性帶來(lái)了仁连,更多的靈活性,可以自由的決定執(zhí)行的時(shí)機(jī)阱穗。
再來(lái)看看并行隊(duì)列如何設(shè)置這一枚舉值饭冬。

let queue = DispatchQueue(label: "com.ffib.blog.concurrent.queue", qos: .utility, attributes:
[.concurrent, .initiallyInactive])
queue.async {
    for i in 0..<5 {
        print(i)
    }
}
queue.async {
    for i in 5..<10 {
        print(i)
    }
}
queue.async {
    for i in 10..<15 {
        print(i)
    }
}
queue.activate()

output:
10
0
5
11
1
6
12
2
7
13
3
8
14
4
9
復(fù)制代碼

延時(shí)執(zhí)行

GCD提供了任務(wù)延時(shí)執(zhí)行的方法,通過(guò)對(duì)已創(chuàng)建的隊(duì)列揪阶,調(diào)用延時(shí)任務(wù)的函數(shù)即可昌抠。其中時(shí)間以DispatchTimeInterval設(shè)置,GCD內(nèi)跟時(shí)間參數(shù)有關(guān)系的參數(shù)都是通過(guò)這一枚舉來(lái)設(shè)置鲁僚。

public enum DispatchTimeInterval : Equatable {

    case seconds(Int)     //秒

    case milliseconds(Int) //毫秒

    case microseconds(Int) //微妙

    case nanoseconds(Int)  //納秒

    case never
}
復(fù)制代碼

在設(shè)置調(diào)用函數(shù)時(shí)炊苫,asyncAfter有兩個(gè)及其相同的方法,不同的地方在于參數(shù)名有所不同冰沙,參照Stack Overflow的解釋侨艾。

wallDeadline 和 deadline,當(dāng)系統(tǒng)睡眠后,wallDeadline會(huì)繼續(xù)拓挥,但是deadline會(huì)被掛起唠梨。例如:設(shè)置參數(shù)為60分鐘,當(dāng)系統(tǒng)睡眠50分鐘侥啤,wallDeadline會(huì)在系統(tǒng)醒來(lái)之后10分鐘執(zhí)行当叭,而deadline會(huì)在系統(tǒng)醒來(lái)之后60分鐘執(zhí)行。

let queue = DispatchQueue(label: "com.ffib.blog.after.queue")

let time = DispatchTimeInterval.seconds(5)

queue.asyncAfter(wallDeadline: .now() + time) {
    print("wall dead line done")
}

queue.asyncAfter(deadline: .now() + time) {
    print("dead line done")
}
復(fù)制代碼

DispatchGroup

如果想等到所有的隊(duì)列的任務(wù)執(zhí)行完畢再進(jìn)行某些操作時(shí)愿棋,可以使用DispatchGroup來(lái)完成科展。

let group = DispatchGroup()
let queue1 = DispatchQueue(label: "com.ffib.blog.queue1", qos: .utility)
let queue2 = DispatchQueue(label: "com.ffib.blog.queue2", qos: .utility)
queue1.async(group: group) {
    for i in 0..<10 {
        print(i)
    }
}
queue2.async(group: group) {
    for i in 10..<20 {
        print(i)
    }
}

//group內(nèi)所有線程的任務(wù)執(zhí)行完畢
group.notify(queue: DispatchQueue.main) {
    print("done")
}

output: 
5
0
6
1
7
2
8
3
9
4
done
復(fù)制代碼

如果想等待某一隊(duì)列先執(zhí)行完畢再執(zhí)行其他隊(duì)列可以使用wait

let group = DispatchGroup()
let queue1 = DispatchQueue(label: "com.ffib.blog.queue1", qos: .utility)
let queue2 = DispatchQueue(label: "com.ffib.blog.queue2", qos: .utility)
queue1.async(group: group) {
    for i in 0..<10 {
        print(i)
    }
}
queue2.async(group: group) {
    for i in 10..<20 {
        print(i)
    }
}
group.wait()
//group內(nèi)所有線程的任務(wù)執(zhí)行完畢
group.notify(queue: DispatchQueue.main) {
    print("done")
}
output:
0
1
2
3
4
5
6
7
8
9
done
復(fù)制代碼

為防止隊(duì)列執(zhí)行任務(wù)時(shí)出現(xiàn)阻塞,導(dǎo)致線程鎖死糠雨,可以設(shè)置超時(shí)時(shí)間才睹。

group.wait(timeout: <#T##DispatchTime#>)
group.wait(wallTimeout: <#T##DispatchWallTime#>)
復(fù)制代碼

DispatchWorkItem

Swift3新增的api,可以通過(guò)此api設(shè)置隊(duì)列執(zhí)行的任務(wù)。先看看簡(jiǎn)單應(yīng)用吧琅攘。通過(guò)DispatchWorkItem初始化閉包垮庐。

let workItem = DispatchWorkItem {
    for i in 0..<10 {
        print(i)
    }
}
復(fù)制代碼

調(diào)用一共分兩種情況,第一種是通過(guò)調(diào)用perform()坞琴,自動(dòng)響應(yīng)閉包哨查。

 DispatchQueue.global().async {
     workItem.perform()
 }
復(fù)制代碼

第二種是作為參數(shù)傳給async方法。

 DispatchQueue.global().async(execute: workItem)
復(fù)制代碼

接下來(lái)我們來(lái)看看DispatchWorkItem的內(nèi)部都有些什么方法和屬性剧辐。

init(qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default,
    block: @escaping () -> Void)
復(fù)制代碼

從初始化方法開(kāi)始寒亥,DispatchWorkItem也可以設(shè)置優(yōu)先級(jí),另外還有個(gè)參數(shù)DispatchWorkItemFlags荧关,來(lái)看看DispatchWorkItemFlags的內(nèi)部組成溉奕。

public struct DispatchWorkItemFlags : OptionSet, RawRepresentable {

    public static let barrier: DispatchWorkItemFlags 

    public static let detached: DispatchWorkItemFlags

    public static let assignCurrentContext: DispatchWorkItemFlags

    public static let noQoS: DispatchWorkItemFlags

    public static let inheritQoS: DispatchWorkItemFlags

    public static let enforceQoS: DispatchWorkItemFlags
}
復(fù)制代碼

DispatchWorkItemFlags主要分為兩部分:

  • 覆蓋
    • noQoS 沒(méi)有優(yōu)先級(jí)
    • inheritQoS 繼承Queue的優(yōu)先級(jí)
    • enforceQoS 覆蓋Queue的優(yōu)先級(jí)
  • 執(zhí)行情況
    • barrier
    • detached
    • assignCurrentContext

執(zhí)行情況會(huì)在下文會(huì)具體描述,先在這留個(gè)坑忍啤。
先來(lái)看看設(shè)置優(yōu)先級(jí)加勤,會(huì)對(duì)任務(wù)執(zhí)行有什么影響。

let queue1 = DispatchQueue(label: "com.ffib.blog.workItem1", qos: .utility)
let queue2 = DispatchQueue(label: "com.ffib.blog.workItem2", qos: .userInitiated)
let workItem1 = DispatchWorkItem(qos: .userInitiated) {
    for i in 0..<5 {
        print(i)
    }
}
let workItem2 = DispatchWorkItem(qos: .utility) {
    for i in 5..<10 {
        print(i)
    }
}
queue1.async(execute: workItem1)
queue2.async(execute: workItem2)

output:
5
0
6
7
8
9
1
2
3
4
復(fù)制代碼

由結(jié)果可見(jiàn)即使設(shè)置了DispatchWorkItem僅僅只設(shè)置了優(yōu)先級(jí)并不會(huì)對(duì)任務(wù)執(zhí)行順序有任何影響同波。
接下來(lái)鳄梅,再來(lái)設(shè)置DispatchWorkItemFlags試試

let queue1 = DispatchQueue(label: "com.ffib.blog.workItem1", qos: .utility)
let queue2 = DispatchQueue(label: "com.ffib.blog.workItem2", qos: .userInitiated)

let workItem1 = DispatchWorkItem(qos: .userInitiated, flags: .enforceQoS) {
    for i in 0..<5 {
        print(i)
    }
}

let workItem2 = DispatchWorkItem {
    for i in 5..<10 {
        print(i)
    }
}

queue1.async(execute: workItem1)
queue2.async(execute: workItem2)
output:
5
0
6
1
7
2
8
3
9
4
復(fù)制代碼

設(shè)置enforceQoS,使優(yōu)先級(jí)強(qiáng)制覆蓋queue的優(yōu)先級(jí)未檩,所以兩個(gè)隊(duì)列呈交替執(zhí)行狀態(tài)戴尸,變?yōu)橥粌?yōu)先級(jí)躏嚎。

DispatchWorkItem也有waitnotify方法矗烛,和DispatchGroup用法相同。

DispatchSemaphore

如果你想同步執(zhí)行一個(gè)異步隊(duì)列任務(wù),可以使用信號(hào)量筒溃。
wait()會(huì)使信號(hào)量減一,如果信號(hào)量大于1則會(huì)返回.success沾乘,否則返回timeout(超時(shí))怜奖,也可以設(shè)置超時(shí)時(shí)間。

func wait(wallTimeout: DispatchWallTime) -> DispatchTimeoutResult
func wait(timeout: DispatchTime) -> DispatchTimeoutResult
復(fù)制代碼

signal()會(huì)使信號(hào)量加一翅阵,返回當(dāng)前信號(hào)量歪玲。

func signal() -> Int
復(fù)制代碼

下面通過(guò)實(shí)例來(lái)看看具體的使用。
先看看不使用信號(hào)量時(shí)掷匠,在文件異步寫入會(huì)發(fā)生什么滥崩。

//初始化信號(hào)量為1
let semaphore = DispatchSemaphore(value: 1)

let queue = DispatchQueue(label: "com.ffib.blog.queue", qos: .utility, attributes: .concurrent)
let fileManager = FileManager.default
let path = NSHomeDirectory() + "/test.txt"
print(path)
fileManager.createFile(atPath: path, contents: nil, attributes: nil)

//循環(huán)寫入,預(yù)期結(jié)果為test4
for i in 0..<5 {
        queue.async {
            do {
                try "test\(i)".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
            }catch {
                print(error)
            }
            semaphore.signal()
        }
    }
}
復(fù)制代碼

<figure>[圖片上傳中...(image-135c2a-1545206459030-1)]

<figcaption></figcaption>

</figure>

發(fā)現(xiàn)寫入的結(jié)果根本不是我們想要的讹语。此時(shí)再使用信號(hào)量試試钙皮。

let semaphore = DispatchSemaphore(value: 1)
let queue = DispatchQueue(label: "com.ffib.blog.queue", qos: .utility, attributes: .concurrent)
let fileManager = FileManager.default
let path = NSHomeDirectory() + "/test.txt"
print(path)
fileManager.createFile(atPath: path, contents: nil, attributes: nil)
for i in 0..<5 {
    //.distantFuture代表永遠(yuǎn)
    if semaphore.wait(wallTimeout: .distantFuture) == .success {
        queue.async {
            do {
                print(i)
                try "test\(i)".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
            }catch {
                print(error)
            }
            semaphore.signal()
        }
    }
}
復(fù)制代碼

<figure>[圖片上傳中...(image-f59274-1545206459030-0)]

<figcaption></figcaption>

</figure>

寫入的結(jié)果符合預(yù)期效果,
我們來(lái)看下for循環(huán)里都發(fā)生了什么。第一遍循環(huán)遇到wait時(shí)短条,此時(shí)信號(hào)量為1导匣,大于0,所以if判斷為true茸时,進(jìn)行寫入操作贡定;當(dāng)?shù)诙檠h(huán)遇到wait時(shí),發(fā)現(xiàn)信號(hào)量為0可都,此時(shí)就會(huì)鎖死線程缓待,直到上一遍循環(huán)的寫入操作完成,調(diào)用signal()方法渠牲,信號(hào)量加一命斧,才會(huì)執(zhí)行寫入操作,循環(huán)以上操作嘱兼。好奇的同學(xué)国葬,可以加上sleep(1),然后打開(kāi)文件夾芹壕,會(huì)發(fā)現(xiàn)test.txt文件從test1不斷加1變?yōu)?code>test4汇四。(ps:寫入文件的方式略顯粗糙,不過(guò)這不是本文討論的重點(diǎn)踢涌,僅用以測(cè)試DispatchSemaphore)

DispatchSemaphore還有另外一個(gè)用法通孽,可以限制隊(duì)列的最大并發(fā)量,通過(guò)前面所說(shuō)的wait()信號(hào)量減一睁壁,signal()信號(hào)量加一背苦,來(lái)完成此操作,正如上文所述例子潘明,其實(shí)達(dá)到的效果就是最大并發(fā)量為一行剂。
如果使用過(guò)NSOperationQueue的同學(xué),應(yīng)該知道maxConcurrentOperationCount钳降,效果是類似的厚宰。

DispatchWorkItemFlags

前面留了個(gè)DispatchWorkItemFlags的坑,現(xiàn)在來(lái)具體看看遂填。

barrier

可以理解為隔離铲觉,還是以文件讀寫為例,在讀取文件時(shí)吓坚,可以異步訪問(wèn)撵幽,但是如果突然出現(xiàn)了異步寫入操作,我們想要達(dá)到的效果是在進(jìn)行寫入操作的時(shí)候礁击,使讀取操作暫停盐杂,直到寫入操作結(jié)束逗载,再繼續(xù)進(jìn)行讀取操作,以保證讀取操作獲取的是文件的最新內(nèi)容况褪。
以上文中的test.txt文件為例撕贞,預(yù)期結(jié)果是:在寫入操作之前,讀取到的內(nèi)容是test4测垛;在寫入操作之后捏膨,讀取到的內(nèi)容是done(即寫入的內(nèi)容)。
先看看不使用barrier的結(jié)果食侮。

let queue = DispatchQueue(label: "com.ffib.blog.queue", qos: .utility, attributes: .concurrent)

let path = NSHomeDirectory() + "/test.txt"
print(path)

let readWorkItem = DispatchWorkItem {
    do {
        let str = try String(contentsOfFile: path, encoding: .utf8)
        print(str)
    }catch {
        print(error)
    }
    sleep(1)
}

let writeWorkItem = DispatchWorkItem(flags: []) {
    do {
        try "done".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
        print("write")
    }catch {
        print(error)
    }
    sleep(1)
}
for _ in 0..<3 {
    queue.async(execute: readWorkItem)
}
queue.async(execute: writeWorkItem)
for _ in 0..<3 {
    queue.async(execute: readWorkItem)
}

output:
test4
test4
test4
test4
test4
test4
write
復(fù)制代碼

結(jié)果不是我們想要的号涯。再來(lái)看看加了barrier之后的效果。

let queue = DispatchQueue(label: "com.ffib.blog.queue", qos: .utility, attributes: .concurrent)

let path = NSHomeDirectory() + "/test.txt"
print(path)

let readWorkItem = DispatchWorkItem {
    do {
        let str = try String(contentsOfFile: path, encoding: .utf8)
        print(str)
    }catch {
        print(error)
    }
}

let writeWorkItem = DispatchWorkItem(flags: .barrier) {
    do {
        try "done".write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
        print("write")
    }catch {
        print(error)
    }
}

for _ in 0..<3 {
    queue.async(execute: readWorkItem)
}
queue.async(execute: writeWorkItem)
for _ in 0..<3 {
    queue.async(execute: readWorkItem)
}

output:
test4
test4
test4
write
done
done
done
復(fù)制代碼

結(jié)果符合預(yù)期的想法锯七,barrier主要用于讀寫隔離链快,以保證寫入的時(shí)候,不被讀取眉尸。

作者:FFIB
鏈接:https://juejin.im/post/5a4c542b6fb9a045211f17ac
來(lái)源:掘金
著作權(quán)歸作者所有域蜗。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處噪猾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霉祸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子袱蜡,更是在濱河造成了極大的恐慌丝蹭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坪蚁,死亡現(xiàn)場(chǎng)離奇詭異奔穿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)敏晤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門贱田,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人茵典,你說(shuō)我怎么就攤上這事湘换。” “怎么了统阿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)筹我。 經(jīng)常有香客問(wèn)我扶平,道長(zhǎng),這世上最難降的妖魔是什么蔬蕊? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任结澄,我火速辦了婚禮哥谷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘麻献。我一直安慰自己们妥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布勉吻。 她就那樣靜靜地躺著监婶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪齿桃。 梳的紋絲不亂的頭發(fā)上惑惶,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音短纵,去河邊找鬼带污。 笑死,一個(gè)胖子當(dāng)著我的面吹牛香到,可吹牛的內(nèi)容都是我干的鱼冀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼悠就,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼千绪!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起理卑,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤翘紊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后藐唠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體帆疟,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年宇立,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踪宠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妈嘹,死狀恐怖柳琢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情润脸,我是刑警寧澤柬脸,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站毙驯,受9級(jí)特大地震影響倒堕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜爆价,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一垦巴、第九天 我趴在偏房一處隱蔽的房頂上張望媳搪。 院中可真熱鬧,春花似錦骤宣、人聲如沸秦爆。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)等限。三九已至,卻和暖如春活逆,著一層夾襖步出監(jiān)牢的瞬間精刷,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工蔗候, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留怒允,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓锈遥,卻偏偏與公主長(zhǎng)得像纫事,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子所灸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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