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
- 具有Lock的 lock() 和 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它们根本就不是一个东西,POSIX是Unix/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