Go语言并发模型的2种编程方案

2019-11-10 08:54:59于丽

2.通过通信的解决方案

所有的解决方案都是简单的封装了一下 SimpleAccount 来实现保护机制。

共享内存的解决方案

又叫 “通过共享内存来通信”。

这种方案暗示了使用锁机制来预防同时访问和修改共享资源。锁告诉其它处理程序这个资源已经被一个处理程序占用了,因此别的处理程序需要排队直到当前处理程序处理完毕。

让我们来看看 LockingAccount 是怎么实现的:


type LockingAccount struct {
  lock    sync.Mutex
  account *SimpleAccount
}

//封装一下 SimpleAccount
func NewLockingAccount(balance int) *LockingAccount {
  return &LockingAccount{account: NewSimpleAccount(balance)}
}

func (acc *LockingAccount) Deposit(amount uint) {
  acc.lock.Lock()
  defer acc.lock.Unlock()
  acc.account.Deposit(amount)
}

func (acc *LockingAccount) Withdraw(amount uint) {
  acc.lock.Lock()
  defer acc.lock.Unlock()
  acc.account.Withdraw(amount)
}

func (acc *LockingAccount) Balance() int {
  acc.lock.Lock()
  defer acc.lock.Unlock()
  return acc.account.Balance()
}

直接明了!注意 lock sync.Lock,lock.Lock(),lock.Unlock()。

这样每次一个附属卡访问银行账号(即共享资源),这个附属卡会自动获得锁直到最后操作完毕。

我们的 LockingAccount 像下面这样使用:

func main() {
  balance := 80
  b := NewBank(bank.NewLockingAccount(balance))
 
  fmt.Println("初始化余额", b.Balance())
 
  done := make(chan bool)
 
  go func() { b.Withdraw(30, "马伊琍"); done <- true }()
  go func() { b.Withdraw(10, "姚笛"); done <- true }()
 
  //等待 goroutine 执行完成
  <-done
  <-done
 
  fmt.Println("-----------------")
  fmt.Println("剩余余额", b.Balance())
}

输出的结果是:

初始化余额 80
[-] 30 马伊琍
[-] 10 姚笛
-----------------
剩余余额 40

现在结果正确了!

在这个例子中第一个处理程序加锁后独享共享资源,其它处理程序只能等待它执行完成。

我们接着看一下执行时的情况,假设马伊琍先拿到了锁:

处理过程
                        ________________
                        _马伊琍_|__姚笛__
        加锁                   ><