主队列&主线程

1,598 阅读1分钟

目的

探究主队列和主线程关系。

过程

1.主队列任务是否一定在主线程执行?

    DispatchQueue.global().async {
        DispatchQueue.main.async {
            let currentThread = Thread.current
            print(currentThread.isMainThread) //打印为true
        }
    }

结论:所以主队列只会在主线程中执行。

2.主线程是否只执行主队列的任务?

DispatchQueue.global().sync {
    let currentThread = Thread.current
    print(currentThread.isMainThread) //打印为true
}

结论:主线程还有可能会执行其他队列的任务。这是为了避免线程切换对性能的消耗。因为CPU的寄存器只有一套,多线程执行时系统势必会不断调度切换。这样每个线程需要一个上下文来记录当前执行状态。这样新线程被执行时首先将上下文写入寄存器,执行结束寄存器重新写入上下文,如此不断切换才避免了多线程的数据混乱。

使用

RxSwift的判断方式:

extension DispatchQueue {
    private static var token: DispatchSpecificKey<()> = {
        let key = DispatchSpecificKey<()>()
        DispatchQueue.main.setSpecific(key: key, value: ())
        return key
    }()

    static var isMain: Bool {
        return DispatchQueue.getSpecific(key: token) != nil
    }
}

Kingfisher中为了提高性能当在主队列主线程中时就直接执行block。因为如果只判断是主队列就执行有可能此时CPU正在处理其他线程任务,如果只判断主线程那么此时执行的任务有可能是其他队列的,并不能保证block添加到执行的队列。

extension DispatchQueue {
    // This method will dispatch the `block` to self.
    // If `self` is the main queue, and current thread is main thread, the block
    // will be invoked immediately instead of being dispatched.
    func safeAsync(_ block: @escaping ()->()) {
        if self === DispatchQueue.main && Thread.isMainThread {
            block()
        } else {
            async { block() }
        }
    }
}

参考

深入iOS系统底层之CPU寄存器介绍