定義:
同步:等帶任務(wù)執(zhí)行完畢再return,在多線程的領(lǐng)域,當(dāng)兩個或兩個以上的線程共享某些資源或需要相互配合來完成某些工作時,就必須通過線程同步來協(xié)調(diào)各個線程運行的次序。
比如在線程A和B配合工作時,A執(zhí)行到一定程度時要依靠B的某個結(jié)果,于是停下來,示意B運行;B依言執(zhí)行,再將結(jié)果給A;A再繼續(xù)操作。或者當(dāng)線程A和B共享一個資源時,如果同一時間讀寫這個資源,就會發(fā)生資源競爭的問題,這時就只能允許某個時間點只有一個線程占有資源,另外一個線程等待,這也是線程同步。
異步:不用等待任務(wù)執(zhí)行完畢就return,如果一個資源被其他線程訪問,在等待訪問的同時可以先去執(zhí)行其他任務(wù)
同步任務(wù):sync不會開啟新的線程,在當(dāng)前線程中執(zhí)行任務(wù),執(zhí)行完一個再執(zhí)行下一個,需要等待
異步任務(wù):async是彼此獨立的,可以不需要等待某一件事完成后再工作,和并行隊列配合使用無法確定任務(wù)的執(zhí)行順序。執(zhí)行異步任務(wù)可能會創(chuàng)建新的線程。
串行隊列:隊列中的任務(wù)一個一個的執(zhí)行,前一個任務(wù)不執(zhí)行完畢,隊列不會調(diào)度
并行隊列:只要有空閑的線程,隊列就會調(diào)度當(dāng)前任務(wù),交給線程去執(zhí)行,不用等待上一個任務(wù)執(zhí)行完畢。
1、常見問題
死鎖場景 Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
let queue = DispatchQueue(label: "queue",qos: .default, attributes: [])
queue.async { //開辟子線程
self.queue.sync { // 不開辟新線程,在當(dāng)前子線程執(zhí)行block,發(fā)生死鎖,外部的block等內(nèi)部的block執(zhí)行完畢才算結(jié)束,內(nèi)部的block等外部的block執(zhí)行完再執(zhí)行自己
}
}
queue.sync {
self.queue.sync {
}
}
// 在主線程中向主隊列中同步添加方法,發(fā)生死鎖,在子線程中添加沒有問題
DispatchQueue.main.sync {
}
2、柵欄,在全局隊列中無效
func dispatchBarrier() {
concurrentQueue.async {
self.printCurrentThread(sort: "1111")
}
concurrentQueue.async(flags: DispatchWorkItemFlags.barrier) {
self.printCurrentThread(sort: "22222")
}
concurrentQueue.async {
self.printCurrentThread(sort: "333333")
}
printCurrentThread(sort: "end")
}
面試題
1、死鎖
print("----111---\(Thread.current)")
queue.async { [weak self] in
print("----222---\(Thread.current)")
self?.queue.sync {
print("----3333---\(Thread.current)")
}
print("----44444---\(Thread.current)")
}
print("---555---\(Thread.current)")
----111---<_NSMainThread: 0x6000023d00c0>{number = 1, name = main}
---555---<_NSMainThread: 0x6000023d00c0>{number = 1, name = main}
----222---<NSThread: 0x6000023d5840>{number = 4, name = (null)}
333和444因為死鎖不能打印
print("----111---\(Thread.current)")
queue.sync { [weak self] in
print("----222---\(Thread.current)")
self?.queue.sync {
print("----3333---\(Thread.current)")
}
print("----44444---\(Thread.current)")
}
print("---555---\(Thread.current)")
----111---<_NSMainThread: 0x6000034dc0c0>{number = 1, name = main}
----222---<_NSMainThread: 0x6000034dc0c0>{number = 1, name = main}
555因為queue.sync阻塞主線程不執(zhí)行,333和444是發(fā)生了死鎖
print("----111---\(Thread.current)")
DispatchQueue.main.async {
print("----2222---\(Thread.current)")
self.semaphore.signal()
}
print("----333---\(Thread.current)")
semaphore.wait()
print("----444---\(Thread.current)")
----111---<_NSMainThread: 0x6000005d00c0>{number = 1, name = main}
----333---<_NSMainThread: 0x6000005d00c0>{number = 1, name = main}
semaphore.wait()一直堵塞主線程,沒被執(zhí)行完畢,導(dǎo)致主隊列中的async任務(wù)無法被調(diào)用
2、其他類型
print("----111---\(Thread.current)")
queue.sync { [weak self] in
print("----222---\(Thread.current)")
self?.queue.async {
print("----3333---\(Thread.current)")
}
print("----44444---\(Thread.current)")
}
print("---555---\(Thread.current)")
----111---<_NSMainThread: 0x600003fe05c0>{number = 1, name = main}
----222---<_NSMainThread: 0x600003fe05c0>{number = 1, name = main}
----44444---<_NSMainThread: 0x600003fe05c0>{number = 1, name = main}
---555---<_NSMainThread: 0x600003fe05c0>{number = 1, name = main}
----3333---<NSThread: 0x600003fbb480>{number = 9, name = (null)}
sync 無論在串行或者并發(fā)隊列中都會阻塞當(dāng)前線程,555后執(zhí)行,async將異步任務(wù)放到出行隊列中,需要等待前一個任務(wù)sync執(zhí)行完畢,在執(zhí)行自己
print("----111---\(Thread.current)")
DispatchQueue.main.async {
while true {
print("----true1---\(Thread.current)")
}
}
DispatchQueue.main.async {
while true {
print("----true2---\(Thread.current)")
}
}
print("----333---\(Thread.current)")
----111---<_NSMainThread: 0x600002e00380>{number = 1, name = main}
----333---<_NSMainThread: 0x600002e00380>{number = 1, name = main}
----true1---<_NSMainThread: 0x600002e00380>{number = 1, name = main}
無限的輸出----true1--,--true2--一直處于等待狀態(tài)
func performSelectedFunc() {
concurrentQueue.async {
print("----1----\(Thread.current)")
self.perform(#selector(self.test), with: nil, afterDelay: 2)
// RunLoop.current.run() //perform執(zhí)行完畢,runloop就關(guān)閉了,放到perform前面無效
print("----3----\(Thread.current)")
}
}
@objc func test() {
print("---2-----\(Thread.current)")
}
----1----<NSThread: 0x600000a72e80>{number = 4, name = (null)}
----3----<NSThread: 0x600000a72e80>{number = 4, name = (null)}
子線程的runloop需要手動開啟
for _ in 0 ... 1000 {
concurrentQueue.async { [unowned self] in
concurrentQueue.sync { [unowned self] in
self.ticketCount += 1
printCurrentThread(sort: "\(ticketCount)")
}
}
}
// 亂序輸出,且ticketCount的最終值小于1001
3、線程同步方案
1、os_unfair_lock
var lock = os_unfair_lock()
for _ in 0 ... 100 {
concurrentQueue.async { [weak self] in
os_unfair_lock_lock(&lock)
self?.ticketCount += 1
print("--------\(self?.ticketCount)")
os_unfair_lock_unlock(&lock)
}
}
2、信號量
for _ in 0 ... 1000 {
concurrentQueue.async { [unowned self] in
self.threadSync()
}
}
func threadSync() {
semaphore.wait()
self.ticketCount += 1
printCurrentThread(sort: "\(ticketCount)")
semaphore.signal()
}
3、串行隊列
for _ in 0 ... 1000 {
concurrentQueue.async { [unowned self] in
self.threadSync()
}
}
func threadSync() {
queue.sync { [unowned self] in
self.ticketCount += 1
printCurrentThread(sort: "\(ticketCount)")
}
}
4、柵欄
for _ in 0 ... 1000 {
concurrentQueue.async { [unowned self] in
self.threadSync()
}
}
func threadSync() {
concurrentQueue.async(flags: DispatchWorkItemFlags.barrier) { [unowned self] in
self.ticketCount += 1
printCurrentThread(sort: "\(ticketCount)")
}
}
4、項目中的應(yīng)用場景
1、多個網(wǎng)絡(luò)請求都返回完畢,再統(tǒng)一刷新UI
func someRequstRefresView() {
let group = DispatchGroup()
func func1() {
self.concurrentQueue.async(group: group) {
Thread.sleep(forTimeInterval: 1)
print("----1---\(Thread.current)")
}
}
func func2() {
self.concurrentQueue.async(group: group) {
Thread.sleep(forTimeInterval: 1)
print("----2---\(Thread.current)")
}
}
func func3() {
self.concurrentQueue.async(group: group) {
Thread.sleep(forTimeInterval: 1)
print("----3---\(Thread.current)")
}
}
func1()
func2()
func3()
group.notify(queue: DispatchQueue.main) {
print("----notify---\(Thread.current)")
self.view.backgroundColor = .red
}
}
2、控制網(wǎng)絡(luò)請求返回的順序
func semaphorefunc() {
print("----begin---\(Thread.current)")
let semaphore = DispatchSemaphore.init(value: 1)
concurrentQueue.async {
semaphore.wait()
print("----1111---\(Thread.current)")
Thread.sleep(forTimeInterval: 1)
semaphore.signal()
}
concurrentQueue.async {
semaphore.wait()
print("----2222---\(Thread.current)")
semaphore.signal()
}
concurrentQueue.async {
semaphore.wait()
print("----3333---\(Thread.current)")
semaphore.signal()
}
print("----end---\(Thread.current)")
}
----begin---<_NSMainThread: 0x600001a985c0>{number = 1, name = main}
----end---<_NSMainThread: 0x600001a985c0>{number = 1, name = main}
----1111---<NSThread: 0x600001ad1140>{number = 43, name = (null)}
----2222---<NSThread: 0x600001ade600>{number = 5, name = (null)}
----3333---<NSThread: 0x600001a98c40>{number = 44, name = (null)}
3、數(shù)據(jù)計算線程安全
print("----begin---\(Thread.current)")
let semaphore = DispatchSemaphore.init(value: 1)
for _ in 0 ... 100 {
concurrentQueue.async { [weak self] in
semaphore.wait()
self?.ticketCount += 1
print("--------\(self?.ticketCount ?? 0)-----\(Thread.current)")
semaphore.signal()
}
}