如何利用NSLock防止功能触发两次? [英] How to utilize NSLock to prevent a function from firing twice?

查看:124
本文介绍了如何利用NSLock防止功能触发两次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前,我有一组异步函数,都在viewDidLoad()中调用.每个函数的末尾都有一个布尔值,该布尔值在函数完成时从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()
}

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

解决方案

一个 NSLock 互斥体;它可以防止多个线程同时访问同一资源,这正是您要在此处执行的操作.一旦一个线程获取了锁,其他尝试获取该锁的线程将等待,直到第一个线程释放该锁.

您将要创建一个锁并将其存储在函数调用之间和函数调用之间持续存在的某个位置,在这种情况下,很可能存储在实例变量中.要获取锁,请调用其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.调度的级别比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()
}

I current have a set of asynchronous functions that are both called in the viewDidLoad(). At the end of each function is a bool that is set from false to true upon completion of the function. There is also a conditional statement checking both function's bools that fires a third function. This conditional statement is in both functions (that I want called when both of the two have finished). Generally:

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

This setup ensures that functionThree() can only be run when both functionOne and functionTwo are done. If functionOne finishes its async stuff before functionTwo() and gets to the conditional to fire functionThree(), it will not do it as checkTwo has not been made true yet. Thus, when functionTwo()'s async stuff is done, it will fire functionThree() . This works appropriately and has not caused an issue once. What I want to expressly avoid, though, is the async functions happening to finish, and therefore calling functionThree(), at the exact same time. To do this I would like to set an NSLock(), but, despite looking up documentation, I have zero clue how to do this as I need the same lock being handled by two different functions. Anyone have any ideas?

解决方案

An NSLock is a mutex; it prevents multiple threads from accessing the same resource simultaneously, which is exactly what you want to do here. Once one thread acquires the lock, other threads attempting to acquire the lock will wait until the first thread releases the lock.

You'll want to create a lock and store it somewhere that persists across and between function calls, most likely in an instance variable in this case. To acquire the lock, call its lock method, and to release it, use 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()
}

A more "modern" approach is to use a DispatchQueue instead of an NSLock. Dispatch is higher-level than APIs like NSLock and NSThread; instead of directly working with locks and threads, you'll use queues.

A serial dispatch queue works like a checkout line at a store. You submit blocks of code to the queue, and it executes them one at a time in the order they were received. You can also create a concurrent dispatch queue which executes its tasks simultaneously by passing .concurrent to the options parameter of the DispatchQueue initializer.

A serial dispatch queue is an easy way to protect a resource from being accessed by multiple threads at once -- just create a queue for that resource, and put every access to that resource on the queue.

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防止功能触发两次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆