CAS算法(比较和交换)
CAS算法是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(非阻塞同步).CAS算法涉及到三个操作数
-
<李>需要读写的内存值V 李>
<李>进行比较的值一个李>
<李>拟写入的新值B 李>
当且仅当V的值等于时,中科院通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
<>强自旋锁强>
自旋锁是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断地判断是否能够被成功获取,知直到获取到锁才会退出循环。
获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成忙等待。
它是为实现保护共享资源而提出的一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能由一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
<强> golang实现自旋锁强>
自旋锁类型uint32 func (sl) *自旋锁锁(){ 对! atomic.CompareAndSwapUint32 ((* uint32) (sl), 0, 1) { runtime.Gosched () } } func (sl) *自旋锁解锁(){ atomic.StoreUint32 ((* uint32) (sl), 0) } func NewSpinLock()同步。储物柜{ var锁自旋锁 返回,锁 } >之前<强>可重入的自旋锁和不可重入的自旋锁强>
文章开始的时候的那段代码,仔细分析一下就可以看的出,它是不支持重入的,即当一个线程第一次已经获取到了该锁,在锁释放之前又一次重新获取该锁,第二次就不能成功获取到。由于不满足CAS,所以第二次获取会进入而循环等待,而如果是可重入锁,第二次也是应该能够成功获取到的。
而且,即使第二次能够成功获取,那么当第一次释放锁的时候,第二次获取到的锁也会被释放,而这是不合理的。
为了实现可重入锁,我们需要引入一个计数器,用来记录获取锁的线程数
自旋锁类型结构{ 主人int int数 } func (sl) *自旋锁锁(){ 我:=GetGoroutineId () 如果自旋锁.owner==我{//如果当前线程已经获取到了锁,线程数增加一,然后返回 sl.count + + 返回 }//如果没获取到锁,则通过中科院自旋 对! atomic.CompareAndSwapUint32 ((* uint32) (sl), 0, 1) { runtime.Gosched () } } func (sl) *自旋锁解锁(){ 如果rl。主人!=GetGoroutineId () { 恐慌(“illegalMonitorStateError”) } 如果sl.count祝辞0{//如果大于0,表示当前线程多次获取了该锁,释放锁通过数减一来模拟 sl.count—— 其他}{//如果数==0,可以将锁释放,这样就能保证获取锁的次数与释放锁的次数是一致的了。 atomic.StoreUint32 ((* uint32) (sl), 0) } } func GetGoroutineId int () { 推迟func () { 如果犯错:=()恢复;犯错!=nil { fmt。Println(“恐慌恢复:恐慌信息:% v”,犯错)} }() var buf[64]字节 n:=运行时。堆栈(buf[:],假) idField:=strings.Fields (strings.TrimPrefix (string (buf [: n]),“goroutine”)) [0] id,犯错:=strconv.Atoi (idField) 如果犯错!=nil { (fmt恐慌。Sprintf(“不能goroutine id: % v”,犯错)) } 返回id } func NewSpinLock()同步。储物柜{ var锁自旋锁 返回,锁 } >之前<>强自旋锁的其他变种强>
<强> 1。TicketLock 强>
TicketLock主要解决的是公平性的问题。
思路:每当有线程获取锁的时候,就给该线程分配一个递增的id,我们称之为排队号,同时,锁对应一个服务号,每当有线程释放锁,服务号就会递增,此时如果服务号与某个线程排队号一致,那么该线程就获得锁,由于排队号是递增的,所以就保证了最先请求获取锁的线程可以最先获取到锁,就实现了公平性。
golang自旋锁的实现