九、GCD

定义:

同步:等带任务执行完毕再return,在多线程的领域,当两个或两个以上的线程共享某些资源或需要相互配合来完成某些工作时,就必须通过线程同步来协调各个线程运行的次序。
比如在线程A和B配合工作时,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。或者当线程A和B共享一个资源时,如果同一时间读写这个资源,就会发生资源竞争的问题,这时就只能允许某个时间点只有一个线程占有资源,另外一个线程等待,这也是线程同步。

异步:不用等待任务执行完毕就return,如果一个资源被其他线程访问,在等待访问的同时可以先去执行其他任务


同步任务:sync不会开启新的线程,在当前线程中执行任务,执行完一个再执行下一个,需要等待
异步任务:async是彼此独立的,可以不需要等待某一件事完成后再工作,和并行队列配合使用无法确定任务的执行顺序。执行异步任务可能会创建新的线程。
串行队列:队列中的任务一个一个的执行,前一个任务不执行完毕,队列不会调度
并行队列:只要有空闲的线程,队列就会调度当前任务,交给线程去执行,不用等待上一个任务执行完毕。

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 {  // 不开辟新线程,在当前子线程执行block,发生死锁,外部的block等内部的block执行完毕才算结束,内部的block等外部的block执行完再执行自己

   }
}

queue.sync {
       self.queue.sync {
   }
}

// 在主线程中向主队列中同步添加方法,发生死锁,在子线程中添加没有问题
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---{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阻塞主线程不执行,333和444是发生了死锁
        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()一直堵塞主线程,没被执行完毕,导致主队列中的async任务无法被调用

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---{number = 9, name = (null)}
sync 无论在串行或者并发队列中都会阻塞当前线程,555后执行,async将异步任务放到出行队列中,需要等待前一个任务sync执行完毕,在执行自己
        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--一直处于等待状态
    func performSelectedFunc() {
        concurrentQueue.async {
            print("----1----\(Thread.current)")
            self.perform(#selector(self.test), with: nil, afterDelay: 2)
//            RunLoop.current.run() //perform执行完毕,runloop就关闭了,放到perform前面无效

            print("----3----\(Thread.current)")
        }
    }
    @objc func test() {
        print("---2-----\(Thread.current)")
    }
----1----{number = 4, name = (null)}
----3----{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、项目中的应用场景

1、多个网络请求都返回完毕,再统一刷新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、控制网络请求返回的顺序

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---{number = 43, name = (null)}
----2222---{number = 5, name = (null)}
----3333---{number = 44, name = (null)}

3、数据计算线程安全

        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()
            }
        }

你可能感兴趣的:(九、GCD)