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

aiqt4smr  于 2023-09-29  发布在  Swift
关注(0)|答案(3)|浏览(124)

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

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还没有被设置为true。因此,当functionTwo()的异步操作完成时,它将触发functionThree()。这工作正常,一次也没有引起问题。不过,我想明确避免的是,异步函数碰巧完成,因此在同一时间调用functionThree()。要做到这一点,我想设置一个NSLock(),但是,尽管查找文档,我没有线索如何做到这一点,因为我需要由两个不同的函数处理相同的锁。有人有什么想法吗?

2w2cym1i

2w2cym1i1#

NSLockmutex;它防止多个线程同时访问同一资源,这正是您在这里要做的。一旦一个线程获得锁,尝试获得锁的其他线程将等待,直到第一个线程释放锁。
您需要创建一个锁,并将其存储在函数调用之间的某个位置,在本例中最有可能是在示例变量中。要获取锁,调用它的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()
}

一种更“现代”的方法是使用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()
}
mqkwyuun

mqkwyuun2#

另一种方法是使用DispatchGroup。更简单,imho。

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
    }
}
suzh9iv8

suzh9iv83#

我认为从Swift 5.7开始,你也可以考虑使用actor来实现线程安全编程。
https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/#Actors

相关问题