golang自旋锁的实现

  

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自旋锁的实现