Go 中的活锁

195 阅读2分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

活锁

活锁是互相谦让,都将自己占有的资源释放给别动线程使用, 这样的资源多个线程之间跳动但是又得不到执行,就形成了活锁。

例如线程1可以使用资源 R1 ,但是很礼貌,让其他线程先使用资源,线程2 可以使用资源 R1 ,但是很绅士,也让其他线程使用资源 R1 ,这样互相让来让去,最后两个线程无法使用资源。

现实生活中的例子

在走廊走向另一个人? 她移动到一边让你通过,但你也是这样做的。 所以你转移到另一边,但她也是这样做的。 想象这会永远持续下去,这就是活锁。

image.png

代码示例:

import (
   "bytes"
   "fmt"
   "sync"
   "sync/atomic"
   "testing"
   "time"
)

func TestLiveLock(t *testing.T) {
   cadence := sync.NewCond(&sync.Mutex{})
   go func() {
      for range time.Tick(1 * time.Millisecond) {
         cadence.Broadcast()
      }
   }()

   takeStep := func() {
      cadence.L.Lock()
      cadence.Wait()
      cadence.L.Unlock()
   }
   //tryDir 允许一个人尝试向一个方向移动,并返回是否成功。dir,每个方向都表示为试图朝这个方向移动的人数。
   tryDir := func(dirName string, dir *int32, out *bytes.Buffer) bool {
      fmt.Fprintf(out, " %v", dirName)
      //首先,我们宣布将要向这个方向移动一个距离。现在,只需要知道这个包的操作是原子操作。
      atomic.AddInt32(dir, 1)
      //为了演示活锁,每个人都必须以相同的速度或节奏移动。takeStep 模拟所有对象之间的一个不变的节奏。
      takeStep()
      if atomic.LoadInt32(dir) == 1 {
         fmt.Fprint(out, ". Success!")
         return true
      }
      takeStep()
      //这里的人意识到他们不能向这个方向走而放弃。我们通过把这个方向减 1 来表示。
      atomic.AddInt32(dir, -1)
      return false
   }

   var left, right int32
   tryLeft := func(out *bytes.Buffer) bool { return tryDir("left", &left, out) }
   tryRight := func(out *bytes.Buffer) bool { return tryDir("right", &right, out) }

   walk := func(walking *sync.WaitGroup, name string) {
      var out bytes.Buffer
      defer func() { fmt.Println(out.String()) }()
      defer walking.Done()
      fmt.Fprintf(&out, "%v is trying to scoot:", name)
      //对尝试次数进行了人为限制,以便此程序能结束。在一个有活锁的程序中,可能没有这个限制,这就是为什么它是一个问题!
      for i := 0; i < 5; i++ {
         //首先,这个人会试图向左走,如果失败了,他们会尝试向右走
         if tryLeft(&out) || tryRight(&out) {
            return
         }
      }
      fmt.Fprintf(&out, "%v tosses her hands up in exasperation!", name)
   }
   //这个变量为程序提供了一个等待直到两个人都能够相互通过或放弃的方式
   var peopleInHallway sync.WaitGroup
   peopleInHallway.Add(2)
   go walk(&peopleInHallway, "Alice")
   go walk(&peopleInHallway, "Barbara")
   peopleInHallway.Wait()
}

执行结果:

=== RUN   TestLiveLock
Alice is trying to scoot: left right left right left right left right left rightAlice tosses her hands up in exasperation!
Barbara is trying to scoot: left right left right left right left right left rightBarbara tosses her hands up in exasperation!
--- PASS: TestLiveLock (0.02s)
PASS