Posted by Maqy on July 7, 2018

简单介绍下iOS下常用的锁操作,加上个人的理解,如有错误欢迎指出:

常用的锁分类:自旋锁,互斥锁,信号量

自旋锁

实现原理应该是全局管理一个锁标志,lock操作会占用锁,如果已经被占用,则会一直循环抢占。unlock会释放锁。

ptheard_lock_t lock; // 类型名是私自起的

// 如果>0则占用锁并做事情,否则继续抢占
lock 
	if lock > 0 {
		lock = 0
        do something
	} else {
        lock()
	}
	
// 释放锁
unlock
	lock = 1

自旋锁有个缺点是如果你没有抢占到锁,会一直占用CPU,所以不是个好东西

互斥锁

互斥锁和自旋锁的不同在于如果没抢占到锁,会挂起,不浪费CPU。

那么要实现这种操作就要借助 生产者消费者模型。

生产消费模型先简单介绍下,下边会详细介绍:

​ 基础逻辑是维护一个队列,如果有生产者过来,会存放在队列里,然后让消费者去处理,也就是消费者从队列里拿出并处理消息,生产者向队列加入消息。

ptheard_lock_t lock; // 类型名是私自起的
ptherad_lock_queue_t theradQueue; // 私自命名,生产消费模型的队列

// 如果>0则占用锁并做事情,否则继续抢占
lock 
	if lock > 0 {
		lock = 0
        do something
	} else {
        theradQueue.addThread()
        sleep()
	}
	
// 释放锁
unlock
	lock = 1
	if theradQueue.count > 0 {
		// 找到在等待的线程并继续执行
        theradQueue.lastObject wakeUp()
	}

互斥锁的好处在于,如果没抢占到锁,那我会休息一会,等别人来叫我。节省CPU。(pthread_mutex)

信号量

个人理解信号量是在互斥锁的基础上做了一个扩展,使之能够控制线程并发数:

ptheard_lock_t lock = 10; // 类型名是私自起的
ptherad_lock_queue_t theradQueue; // 私自命名,生产消费模型的队列

// 如果>0则占用锁并做事情,否则继续抢占
lock 
	if lock > 0 {
		lock--
        do something
	} else {
        theradQueue.addThread()
        sleep()
	}
	
// 释放锁
unlock
	lock++
	if theradQueue.count > 0 {
		// 找到在等待的线程并继续执行
        theradQueue.lastObject wakeUp()
	}

这样就可以控制线程并发量了,也就是把锁的变量值控制在一个数,也就是并发数。

生产者消费模型

那么什么是生产消费模型呢?

其实就是建立一个缓冲区,来记录下现在来不及处理,但需要按顺序来处理的queue。

// 为了方便理解 采用部分语言的方式书写
class ptherad_lock_queue_t {
    MutableArray *queue = []
    
    void addThread(Thread *thread) {
        queue.insertObejctAtIndex(thread, 0)
    }
    
    Thread *lastObject() {
        return queue.lastObject()
    }
}

我们可以把这个理解成一个缓冲区:
当有线程想加锁,但没有抢到时,会加在这个队列里。
当上一个队列释放锁之后,会唤醒队列里出栈的那个。
这样就能保证线程间按顺序执行了。