【问题标题】:How to utilize NSLock to prevent a function from firing twice?如何利用 NSLock 防止函数触发两次?
【发布时间】:2017-07-19 23:27:23
【问题描述】:

我目前有一组异步函数,它们都在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()。这可以正常工作,并且一次没有引起问题。不过,我要明确避免的是异步函数恰好完成,因此同时调用functionThree()准确。为此,我想设置一个NSLock(),但是,尽管查找了文档,但我对如何执行此操作的线索为零,因为我需要由两个不同的函数处理相同的锁。有人有什么想法吗?

【问题讨论】:

    标签: swift asynchronous nslock


    【解决方案1】:

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

    【讨论】:

    • 很好的回应,但我有几个问题。首先是关于NSLock 方法。它非常简单,但假设functionOne() 运行得足够多,以至于它调用了lock.lock(),但还没有调用lock.unlock()。如果functionTwo() 然后到达它的lock.lock(),它还不能继续(对吗?)。我的问题是,functionTwo() 基本上只是在那里等待,然后在可以的时候继续?即它是否在线lock.lock() 直到functionOne() 解锁锁,然后functionTwo() 继续? (对不起,我只是想了解到底发生了什么)。
    • 至于DispatchQueue方法,queue.sync到底在做什么?一次只允许一个代码行通过queue.sync,从而防止两者同时触发?如果他们确实同时达到了那个点,会发生什么?他们都没有继续,或者是随机选择的,等等......?
    • @AlekPiasecki 请看我的编辑。 (如果两个线程同时到达锁,则保证其中一个线程继续执行;我不相信哪个线程继续执行。)
    【解决方案2】:

    使用 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
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-23
      • 2015-03-18
      • 2020-03-17
      • 1970-01-01
      • 2017-07-05
      • 1970-01-01
      • 2022-07-20
      相关资源
      最近更新 更多