Swift-线程锁基础

3,151 阅读4分钟

本篇内容多线程相关

一.锁的分类

种类:

NSLock,NSConditionLock,NSRecursiveLock, NSCondition.

对比

  • 都遵循NSLocking协议.
public protocol NSLocking {
    func lock()
    func unlock()
}
  • 除了NSCondition,其它三种锁都是有以下两个方法
func `try`() -> Bool

func lock(before limit: Date) -> Bool

二.锁的定义,使用及注意点**

1.NSLock

定义

在同一应用程序中协调多个执行线程的操作的对象。

使用

NSLock对象可以用来作为对应用程序全局数据的中间访问,或者用来保护代码的关键部分,允许它自动运行。

注意点

  • 加锁解锁必须在同一线程.
  • 不应该使用这个类来实现递归锁。在同一个线程上调用两次锁方法将会永久锁定线程。
  • 解锁未锁定的锁被认为是程序员的错误,应该在代码中修复。当错误发生时,NSLock类通过向控制台打印错误消息来报告这些错误。

测试

  • 1.基本使用,锁住关键代码.
  • 2.tryLock方法尝试获取锁,但如果锁不可用,则不会阻塞;相反,该方法只返回NO.
  • 3.lockBeforeDate方法尝试获取锁,但是如果在指定的时间限制内没有获取锁,则解除线程阻塞(并返回NO)。

代码测试:

1.的测试

//在多线程中调用测试:
    func saleTickets() {
        //加锁
        lock.lock()
        sleep(2)
        if ticket > 0{
            ticket -= 1
            print(Date(),"当前票数\(ticket)")
        }else{
            print("没有票了")
            //解锁
            lock.unlock()
            return
        }
        //解锁
        lock.unlock()
    }

2.的测试

func testTryLock(){
        let lock = NSLock()
        DispatchQueue.global().async {
            lock.lock()
            print(Date(),"线程1执行")
            sleep(2)
            lock.unlock()
            print(Date(),"线程1解锁")
        }
        DispatchQueue.global().async {
            sleep(1)
            if lock.try(){
                print(Date(),"线程2执行")
            }else{
                print(Date(),"线程2加锁失败")
            }
        }
    }

打印结果:

2019-10-31 09:50:09 +0000 线程1执行
2019-10-31 09:50:10 +0000 线程2加锁失败
2019-10-31 09:50:11 +0000 线程1解锁

3.的测试

    func testLockBefor(){
        let lock = NSLock()
        DispatchQueue.global().async {
            lock.lock()
            print(Date(),"线程1执行")
            //分别测试2,8,观察结果.
            sleep(2)
            lock.unlock()
            print(Date(),"线程1解锁")
        }
        DispatchQueue.global().async {
            sleep(1)
            if lock.lock(before: Date.init(timeIntervalSinceNow: 4)){
                print(Date(),"线程2执行")
            }else{
                print(Date(),"线程2加锁失败")
            }
        }
    }

2.NSConditionLock

定义

可以与特定的用户定义条件相关联的锁。

使用

使用NSConditionLock对象,您可以确保只有在满足特定条件时,线程才能获得锁。一旦获得了锁并执行了代码的关键部分,线程就可以放弃锁并将相关条件设置为新的内容。这些条件本身是任意的:您可以根据应用程序的需要来定义它们。

测试

//代码如下:
    func testNSConditionLock(){
        let lock = NSConditionLock.init(condition: 0)
        DispatchQueue.global().async {
            if lock.tryLock(whenCondition: 0){
                print(Date(),"线程1执行")
                sleep(1)
                lock.unlock(withCondition: 1)
            }else{
                print("线程1枷锁失败")
            }
        }
        DispatchQueue.global().async {
            lock.lock(whenCondition: 3)
            print(Date(),"线程2执行")
            sleep(1)
            lock.unlock(withCondition: 2)
        }
        DispatchQueue.global().async {
            lock.lock(whenCondition: 1)
            print(Date(),"线程3执行")
            sleep(1)
            lock.unlock(withCondition: 3)
        }
    }

3.NSRecursiveLock

定义

同一线程可以多次获得而不会导致死锁的锁。

使用

NSRecursiveLock定义了一个锁,它可以被同一个线程多次获得而不会导致死锁,这种情况下,一个线程被永久阻塞,等待它自己放弃一个锁。虽然锁定线程有一个或多个锁,但所有其他线程都不能访问该锁保护的代码。

测试

    func testRecursiveLock(){
        printPattern(n: 10)
    }
    func printPattern(n : Int) {
        //nsLock.lock()//NSLock死锁
        recursiveLock.lock()
        if n <= 0 {
            print(n)
        }
        else {
            print(n)
            printPattern(n: n - 5)
            print(n)
        }
        //nsLock.unlock()
        recursiveLock.unlock()
    }

4.NSCondition

定义

A condition variable whose semantics follow those used for POSIX-style conditions.

使用

条件对象同时充当给定线程中的锁和检查点。锁在测试条件并执行条件触发的任务时保护代码。检查点行为要求在线程执行其任务之前条件为true。当条件不为真时,线程阻塞。它保持阻塞状态,直到另一个线程发出条件对象的信号。

测试

1.锁住关键代码,通过条件控制线程的进行.

        let nsCondition =  NSCondition()
        //
        var products: [NSObject] = []
        DispatchQueue.global().async {
            while(true){
                nsCondition.lock()
                if products.count == 0{
                    print(Date(),"消费1缺货等待")
                    nsCondition.wait()
                }
                print(Date(),"消费1执行")
                products.removeFirst()
                nsCondition.unlock()
            }
        }
        DispatchQueue.global().async {
            while(true){
                nsCondition.lock()
                print(Date(),"生产线程执行")
                products.append(NSObject())
                nsCondition.signal()
                nsCondition.unlock()
                sleep(1)
            }
        }

2.唤醒其它一个或多个线程

        let nsCondition =  NSCondition()
        DispatchQueue.global().async {
            nsCondition.lock()
            print("线程1加锁成功", Thread.current)
            nsCondition.wait()
            print("线程1")
            nsCondition.unlock()
            print("线程1解锁成功", Thread.current)
        }
        DispatchQueue.global().async {
            nsCondition.lock()
            print("线程2加锁成功", Thread.current)
            nsCondition.wait()
            print("线程2")
            nsCondition.unlock()
            print("线程2解锁成功", Thread.current)
        }
        DispatchQueue.global().async {
            sleep(2)
            //唤醒一个等待的线程
//            print("唤醒一个等待的线程")
//            nsCondition.signal()
            //唤醒所有等待的线程
            print("唤醒所有等待的线程")
            nsCondition.broadcast()
        }