如何利用 NSLock 来防止函数触发两次?

How to utilize NSLock to prevent a function from firing twice?

我目前有一组异步函数,它们都在 viewDidLoad() 中调用。每个函数的末尾都有一个布尔值,在函数完成时将其从 false 设置为 true。还有一个条件语句检查触发第三个函数的两个函数的布尔值。这个条件语句在两个函数中都有(我想在两个函数的 both 完成时调用)。一般:

var checkOne = false
var checkTwo = false

func functionOne(){
    //async stuff
    checkOne = true
    if checkOne == true && checkTwo == true{
        functionThree()//will only run if both functionOne and functionTwo have been completed
    }
}

func functionTwo(){
    //async stuff
    checkTwo = true
    if checkOne == true && checkTwo == true{
        functionThree()//will only run if both functionOne and functionTwo have been completed
    }
}

func functionThree(){
    //stuff
}


override func viewDidLoad() {

    functionOne()
    functionTwo()
}

此设置确保 functionThree() 只能是 运行 当 functionOnefunctionTwo 都完成时。如果 functionOnefunctionTwo() 之前完成它的异步内容并到达触发 functionThree() 的条件,它不会执行它,因为 checkTwo 尚未变为真。因此,当 functionTwo() 的异步内容完成时,它将触发 functionThree() 。这可以正常工作,并且一次都没有引起问题。不过,我想明确避免的是异步函数碰巧完成,因此在 exact 同时调用 functionThree()。为此,我想设置一个 NSLock(),但是,尽管查找了文档,但我对如何执行此操作一无所知,因为我需要由两个不同的函数处理同一个锁。有人有什么想法吗?

一个NSLock is a mutex;它防止多个线程同时访问同一个资源,这正是您在这里想要做的。一旦一个线程获取了锁,其他试图获取锁的线程将等待,直到第一个线程释放锁。

您需要创建一个锁并将其存储在函数调用之间和函数调用之间持续存在的某个位置,在本例中很可能是在一个实例变量中。要获取锁,调用它的 lock 方法,释放它,使用 unlock:

var checkOne = false
var checkTwo = false

//create the lock
let lock = NSLock()

func functionOne(){
    //async stuff
    //acquire the lock
    lock.lock()
    checkOne = true
    if checkOne == true && checkTwo == true{
        functionThree()//will only run if both functionOne and functionTwo have been completed
    }
    //release the lock
    lock.unlock()
}

func functionTwo(){
    //async stuff
    lock.lock()
    checkTwo = true
    if checkOne == true && checkTwo == true{
        functionThree()//will only run if both functionOne and functionTwo have been completed
    }
    lock.unlock()
}

func functionThree(){
    //stuff
}


override func viewDidLoad() {

    functionOne()
    functionTwo()
}

一种更 "modern" 的方法是使用 DispatchQueue 而不是 NSLock。 Dispatch 比 NSLock 和 NSThread 等 API 更高级;您将使用队列而不是直接使用锁和线程。

串行调度队列就像商店的结账线一样工作。您将代码块提交到队列,它会按照收到的顺序一次一个地执行它们。您还可以通过将 .concurrent 传递给 DispatchQueue 初始化程序的 options 参数来创建同时执行其任务的并发调度队列。

串行调度队列是一种保护资源不被多个线程同时访问的简单方法 -- 只需为该资源创建一个队列,并将对该资源的每次访问都放在队列中。

var checkOne = false
var checkTwo = false

//Create a serial dispatch queue
let queue = DispatchQueue(label: "name of queue")

func functionOne(){
    //async stuff

    //Add a task to the queue, and execute it synchronously (i.e. wait for it to finish.)
    //You can also use async to execute a task asynchronously,
    //but sync is slightly more efficient unless you need it to be asynchronous.
    queue.sync {
        checkOne = true
        if checkOne == true && checkTwo == true{
            functionThree()//will only run if both functionOne and functionTwo have been completed
        }
    }
}

func functionTwo(){
    //async stuff
    queue.sync {
        checkTwo = true
        if checkOne == true && checkTwo == true{
           functionThree()//will only run if both functionOne and functionTwo have been completed
        }
    }
}

func functionThree(){
    //stuff
}


override func viewDidLoad() {

    functionOne()
    functionTwo()
}

另一种使用 DispatchGroup 的方法。更简单,恕我直言。

class ViewController: UIViewController {

    let group = DispatchGroup()

    override func viewDidLoad() {
        super.viewDidLoad()

        group.enter()
        functionOne()

        group.enter()
        functionTwo()

        group.notify(queue: .global(qos: .default), execute: { [weak self] in
            self?.functionThree()
        })
    }

    func functionOne() {
        //async stuff

        group.leave()
    }

    func functionTwo() {
        //async stuff

        group.leave()
    }

    func functionThree() {
        //stuff
    }
}