Swift系列三十一 - 多线程

1,283 阅读2分钟

多线程在Swift中也是首先使用GCD。

一、异步

1.1. GCD开启异步线程

示例代码:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        print("主线程", Thread.current)
        
        DispatchQueue.global().async {
            print("子线程", Thread.current)
            DispatchQueue.main.async {
                print("回到主线程", Thread.current)
            }
        }
    }
}
/*
 输出:
 主线程 <NSThread: 0x600000bdc6c0>{number = 1, name = main}
 子线程 <NSThread: 0x600000bb4c80>{number = 6, name = (null)}
 回到主线程 <NSThread: 0x600000bdc6c0>{number = 1, name = main}
 */ 

提示:之前OC中很多类都是NS前缀,但是Swift中和OC对等的类名大部分把NS去掉了。

1.2. GCD任务-DispatchWorkItem

DispatchWorkItem是定义任务的,任务完成后可以通过notify通知其他线程(一般是主线程)做其他任务。

示例代码:

// 定义任务
public typealias Task = () -> Void

public struct Async {
    // 异步线程1:传入子线程任务
    public static func async (_ task: @escaping Task) {
        _async(task)
    }

    // 异步线程2:分别传入子线程和主线程的任务
    public static func async(_ task: @escaping Task, _ mainTask: @escaping Task) {
        _async(task, mainTask)
    }

    public static func _async(_ task: @escaping Task, _ mainTask: Task? = nil) {
        let item = DispatchWorkItem(block: task)
        // 子线程执行任务
        DispatchQueue.global().async(execute: item)
        if let main = mainTask {
            // 子线程执行完毕后通知主线程执行任务
            item.notify(queue: DispatchQueue.main, execute: main)
        }
    }
}

二、延迟

2.1 普通延迟

在iOS中使用延迟一般使用dispatch_after,但是在Swift中没有这个API,使用的是DispatchQueue.*.asyncAfter

示例代码:

@discardableResult
public static func delay(_ seconds: Double, _ block: @escaping Task) -> DispatchWorkItem {
    let item = DispatchWorkItem(block: block)
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
    return item
}

2.2. 异步延迟

示例代码(定义函数):

@discardableResult
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task) -> DispatchWorkItem {
    return _asyncDelay(seconds, task)
}

@discardableResult
public static func asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: @escaping Task) -> DispatchWorkItem {
    return _asyncDelay(seconds, task, mainTask)
}

private static func _asyncDelay(_ seconds: Double, _ task: @escaping Task, _ mainTask: Task? = nil) -> DispatchWorkItem {
    let item = DispatchWorkItem(block: task)
    DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds, execute: item)
    if let main = mainTask {
        item.notify(queue: DispatchQueue.main, execute: main)
    }
    return item
}

示例代码(使用):

class ViewController: UIViewController {
    private var item: DispatchWorkItem?

    override func viewDidLoad() {
        super.viewDidLoad()
           
        item = Async.asyncDelay(3) {
            
        };
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        item?.cancel()
    }
}

为什么定义延迟异步时函数要返回DispatchWorkItem?是因为有可能需要对任务做取消或其他操作。

三、once

dispatch_once在Swift中已被废弃,取而代之的是静态属性(底层还是调用了dispatch_once)。

示例代码:

class ViewController: UIViewController {
    static var onceTask: Void = {
        print("onceTask")
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let _ = Self.onceTask
        let _ = Self.onceTask
        let _ = Self.onceTask
    }
}
// 输出:print("onceTask")

四、加锁

当很多资源同时访问同一块数据的时候,可能会发生抢夺资源的情况,这时候需要一个加锁机制,当上次任务还未结束时,等待到任务完成才能继续下一次任务。

示例代码:

public struct Cache {
    private static var data = [String: Any]()
    private static var lock = DispatchSemaphore(value: 1)
    public static func set(_ key: String, _ value: Any) {
        lock.wait()
        defer {
            lock.signal()
        }
        data[key] = value
    }
}

lock.wait()是加锁,lock.signal()是取消锁(解锁)。加锁的API还有很多:NSLock、NSRecursiveLock等等,使用起来都很简单(只需要注意死锁问题即可)。

更多系列文章,请关注 微信公众号【1024星球】