Swift 线程锁

Lock

  • lock()unlock 一定要成对出现
  • try() 判断是否能加锁
  • 以下为两个线程移除数组的示例
  • 结论:当上锁后只有当 unlock 后线程才会继续执行,保证线程安全。
class Test {
    private let lock = NSLock()
    private var array: [Int] = []
    private lazy var thread0: Thread = {
        let thead = Thread.init(target: self, selector: #selector(remove), object: nil)
        thead.name = "thread000"
        return thead
    }()
    private lazy var thread1: Thread = {
        let thead = Thread.init(target: self, selector: #selector(remove), object: nil)
        thead.name = "thread111"
        return thead
    }()

    init() {
        for i in 0..<100 {
            array.append(i)
        }
    }

    func start() {
        DispatchQueue.global().async {
            self.thread0.start()
            self.thread1.start()
        }
    }

    @objc private func remove() {
        print("=======================:\(Thread.current)")
        var times = 0
        if lock.try() {
            lock.lock()
        }
        defer {
            lock.unlock()
        }
        print("----------------------:\(Thread.current)")
        while array.count > 0 && times < 50 {
            times += 1
            print("start:\(Thread.current)")
            array.removeLast()
            print("count::::\(array.count)")
            print("end:\(Thread.current)\n")
        }

        print("executing:::\(times) \n \(array) \n")
    }
}

let t = Test()
t.start()

NSCondition

  • 具有Locklock()unlock() 方法, 且用法相同。
  • 具有 wait()signal()/broadcast() 两个方法。
  • wait() 阻塞当前线程。
  • signal()通知释放第一阻塞的线程。
  • broadcast() 释放每个线程中的第一个阻塞。
  • 问题:示例中两个线程,总会有一个线程的最后一个阻塞无法释放。请大家评论解答。万分感谢

class Test {
    private let lock = NSCondition()
    private var array: [Int] = []
    private lazy var thread0: Thread = {
        let thead = Thread.init(target: self, selector: #selector(remove), object: nil)
        thead.name = "thread000"
        return thead
    }()
    private lazy var thread1: Thread = {
        let thead = Thread.init(target: self, selector: #selector(remove), object: nil)
        thead.name = "thread111"
        return thead
    }()

    init() {
        for i in 0..<100 {
            array.append(i)
        }
    }

    func start() {
        self.thread0.start()
        self.thread1.start()

        DispatchQueue.global().asyncAfter(deadline: .now() + 2, execute: {
            self.lock.broadcast()
        })
        DispatchQueue.global().asyncAfter(deadline: .now() + 3, execute: {
            self.lock.broadcast()
        })
        DispatchQueue.global().asyncAfter(deadline: .now() + 4, execute: {
            self.lock.broadcast()
        })
        DispatchQueue.global().asyncAfter(deadline: .now() + 5, execute: {
            self.lock.broadcast()
        })
        DispatchQueue.global().asyncAfter(deadline: .now() + 6, execute: {
            self.lock.broadcast()
        })
    }

    @objc private func remove() {
        print("=======================:\(Thread.current)")
        var times = 0
        defer {
            lock.wait()
            print("over::\(Thread.current)")
        }

        lock.wait()
        print("abc0\(Thread.current)")
        lock.wait()
        print("abc1\(Thread.current)")
        lock.wait()
        print("abc2\(Thread.current)")
        lock.wait()

        print("----------------------:\(Thread.current)")
        while array.count > 0 && times < 50 {
            times += 1
            print("start:\(Thread.current)")
            array.removeLast()
            print("count::::\(array.count)")
            print("end:\(Thread.current)\n")
        }
        print("executing:::\(times) \n \(array) \n")
    }
}

let t = Test()
t.start()

NSConditionLock

  • NSConditionLock同样实现了NSLocking协议,不过测试一下之后你会发现这个性能比较低。NSConditionLock也能像NSCondition一样能进行线程之间的等待调用,并且还是线程安全的。
  • NSConditionLock(condition: 2) 条件锁。
  • lock(whenCondition: 2) 满足 == 2 的条件加锁。
  • unlock(withCondition: 2) 满足 == 2 的条件解锁。

class Test {
    private let lock = NSConditionLock(condition: 2)
    private var array: [Int] = []
    private lazy var thread0: Thread = {
        let thead = Thread.init(target: self, selector: #selector(remove), object: nil)
        thead.name = "thread000"
        return thead
    }()
    private lazy var thread1: Thread = {
        let thead = Thread.init(target: self, selector: #selector(remove), object: nil)
        thead.name = "thread111"
        return thead
    }()

    init() {
        for i in 0..<100 {
            array.append(i)
        }
    }

    func start() {
        self.thread0.start()
        self.thread1.start()
    }

    @objc private func remove() {
        print("=======================:\(Thread.current)")
        var times = 0
        if lock.tryLock(whenCondition: 2) {
            print("willlock")
            lock.lock(whenCondition: 2)
        }
        defer {
            lock.unlock(withCondition: 2)
        }
        print("----------------------:\(Thread.current)")
        while array.count > 0 {
            times += 1
            print("start:\(Thread.current)")
            array.removeLast()
            print("count::::\(array.count)")
            print("end:\(Thread.current)\n")
        }

        print("executing:::\(times) \n \(array) \n")
    }
}

let t = Test()
t.start()

NSRecursiveLock

  • 有时候“加锁代码”中存在递归调用,递归开始前加锁,递归调用开始后会重复执行此方法以至于反复执行加锁代码最终造成死锁,这个时候可以使用递归锁来解决,也就是我们的NSRecursiveLock,它就是递归锁!使用递归锁可以在一个线程中反复获取锁而不造成死锁,在这个过程中也会记录获取锁和释放锁的次数,只有等两者平衡的时候才会释放。
  • 同一线程对同一资源可以多次过去。避免其它线程获取;A线程递归。
  • 网上也有说需要避免使用递归锁。·递归锁是不被提倡的,用到递归锁说明这个代码设计是有问题的。
class Test {
    private let lock = NSRecursiveLock()
    private var result = 0

    func start() {
        DispatchQueue.global().async {
            self.result = self.remove(100)
            print("result:\(self.result)")
        }
    }

    @objc private func remove(_ count: Int) -> Int {
        lock.lock()
        if count >= 0 {
            let a = remove(count - 1)
            lock.unlock()
            print("\(a)")
            return  a + count
        }
        return 1
    }
}

let t = Test()
t.start()

@synchronized

OC 中的线程互斥锁方法。Swift 对应使用 objc_sync_enter(self)objc_sync_exit(self)
方法中的参数只能使 self,使用其它的不能达到互斥锁的目的。


class Test {
    private var array: [Int] = []
    init() {
        for i in 0..<100 {
            array.append(i)
        }
    }
    func start() {
        DispatchQueue.global().async {
            self.remove()
        }
        DispatchQueue.global().async {
            self.remove()
        }
    }

    @objc private func remove() {
        print("\(Thread.current)::\(array.count)")
        // 互斥锁
        objc_sync_enter(self)
        while array.count > 0 {
            array.removeLast()
            print("\(Thread.current)::\(array.count)")
        }
        print("\(Thread.current)::\(array.count)")
        objc_sync_exit(self)
    }
}

let t = Test()
DispatchQueue.global().async {
    t.start()
}

dispatch_semaphore_t 信号量

dispatch_semaphore_t是属于GCD里面的东西,在前面总结多线程的时候我们说把它放在我们总结线程锁的时候说,在这里我们就说一些这个信号量,dispatch_semaphore_t 和前面@synchronized一样都是我们OC的写法,在我们的Swift中也不是这样写的,全部的内容都是在DispatchSemaphore中,关于GCD方面API的对比我们在下面做了一张表。
DispatchSemaphore,信号量量 >=0 时候不会阻塞当前线程。信号量小于 0 时候回阻塞当前线程。wait() 信号量减1,signal() 信号量加1
DispatchSemaphore ,可以设置锁的失效时间。和初始信号量

在这里插入图片描述

  • 代码分析

class Test {
    // value >= 0 可以执行;每次 wait(), 信号量 -1 ;每次 signal() 信号量 + 1;
    let semaphare = DispatchSemaphore(value: 1)

    private var array: [Int] = []
    init() {
        for i in 0..<100 {
            array.append(i)
        }
    }
    func start() {
        DispatchQueue.global().async {
            self.remove()
        }
        DispatchQueue.global().async {
            self.remove()
        }
    }

    @objc private func remove() {
        print("\(Thread.current)::\(array.count)")
        // wait() 执行后信号量 -1;因为初始化为 1,减一后为 0,所以可以继续执行。
        // 如果初始信号量为0,减一后为 -1,则立即阻塞当先线程
        semaphare.wait()
        print("start")
        while array.count > 0 {
            array.removeLast()
            print("\(Thread.current)::\(array.count)")
        }
        print("\(Thread.current)::\(array.count)")
        semaphare.signal()
    }
}

let t = Test()
DispatchQueue.global().async {
    t.start()
}

POSIX

POSIX和我们前面写的dispatch_semaphore_t用法是挺像的,但探究本质的haul它们根本就不是一个东西,POSIXUnix/Linux平台上提供的一套条件互斥锁的API。你要是在OC的文件中只用的话你需要导入头文件:pthread.h

Swift中就不用了,但是在使用的时候不管是OC的还是Swift的,代码是一致的,它的几个主要的方法就是下面三个。

主要方法

pthread_mutex_init 初始化方法。
pthread_mutex_lock 加锁方法。
pthread_mutex_unlock 解锁方法。
pthread_mutex_destroy 释放。 犹豫是 POSIX 中的方法所以一定要注意内存是释放。


class Test {
    var mutex: pthread_mutex_t = pthread_mutex_t()

    private var array: [Int] = []
    init() {
        // 初始化
        pthread_mutex_init(&mutex,nil)
        for i in 0..<100 {
            array.append(i)
        }
    }
    func start() {
        DispatchQueue.global().async {
            self.remove()
        }
        DispatchQueue.global().async {
            self.remove()
        }
    }

    @objc private func remove() {
        // 加锁
        pthread_mutex_lock(&mutex)
        print("\(Thread.current)::\(array.count)")
        while array.count > 0 {
            array.removeLast()
            print("\(Thread.current)::\(array.count)")
        }
        print("\(Thread.current)::\(array.count)")
        // 解锁
        pthread_mutex_unlock(&mutex)
    }

    deinit {
        pthread_mutex_destroy(&mutex);  //释放该锁的数据结构
    }
}

let t = Test()
DispatchQueue.global().async {
    t.start()
}

其它

dispatch_barrier_async ~> DispatchIO

- (void)barrier
{  //同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用
    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
}
//输出结果:1 2 --> barrier -->3 4  其中12 与 34 由于并行处理先后顺序不定

dispatch_apply

dispatch_block_notify

dispatch_group_notify ~> DispatchGroup

  • 使用时后要在同一个线程中执行。
  • 使用场景: 一般为文件读取等耗时操作中只用
class Test {
    func start() {

        let queue = DispatchQueue(label: "serialze")
        let group = DispatchGroup()
        group.enter()
        DispatchQueue.global().async {
            for i in 0..<100 {
                queue.async {
                    print("\(i)")
                }
            }
            group.leave()
        }

        group.notify(queue: queue, work: DispatchWorkItem(block: {
            print("finish!!!")
        }))
    }
}

let t = Test()
DispatchQueue.global().async {
    t.start()
}

OSSpinLock

  • 首先要提的是OSSpinLock已经出现了BUG,导致并不能完全保证是线程安全,所以这个我们知道,大概的了解一下,具体的问题可以去这里仔细看看:不再安全的 OSSpinLock
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Swift 的标准字典类型 `Dictionary` 并不是线程安全的,因此在并发场景下使用可能会出现问题。为了实现线程安全的字典,可以使用 `DispatchQueue` 和 `ConcurrentDictionary`。 下面是一个简单的线程安全字典的实现: ```swift final class ConcurrentDictionary<Key: Hashable, Value> { private let queue = DispatchQueue(label: "ConcurrentDictionary", attributes: .concurrent) private var dictionary: [Key: Value] = [:] subscript(key: Key) -> Value? { get { var result: Value? queue.sync { result = dictionary[key] } return result } set(newValue) { queue.async(flags: .barrier) { self.dictionary[key] = newValue } } } var keys: [Key] { var result: [Key] = [] queue.sync { result = Array(dictionary.keys) } return result } var values: [Value] { var result: [Value] = [] queue.sync { result = Array(dictionary.values) } return result } } ``` 这个字典的实现使用了一个 `DispatchQueue` 来保证对字典的访问是线程安全的。`ConcurrentDictionary` 实现了 `subscript`、`keys` 和 `values` 方法,它们都采用了不同的 dispatch 方法来执行读取和写入操作。其中,`subscript` 方法采用了 `sync` 方法,它会等待队列中所有任务执行完毕后才返回结果;而 `keys` 和 `values` 方法使用了 `async` 方法并传递了 `.barrier` 标志,它会等待队列中所有读取操作完成后再执行写入操作。 使用时,可以像使用普通字典一样使用这个线程安全字典,例如: ```swift let dict = ConcurrentDictionary<String, Int>() dict["one"] = 1 dict["two"] = 2 print(dict.keys) print(dict.values) ``` 需要注意的是,这个线程安全字典的实现并不是最优的,如果需要更高的性能,可以考虑使用更复杂的数据结构,例如 `ConcurrentHashmap`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值