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

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

概述

我一直在找一种好的方法来解释 go 语言的并发模型:

不要通过共享内存来通信,相反,应该通过通信来共享内存

但是没有发现一个好的解释来满足我下面的需求:

1.通过一个例子来说明最初的问题
2.提供一个共享内存的解决方案
3.提供一个通过通信的解决方案

这篇文章我就从这三个方面来做出解释。

读过这篇文章后你应该会了解通过通信来共享内存的模型,以及它和通过共享内存来通信的区别,你还将看到如何分别通过这两种模型来解决访问和修改共享资源的问题。

前提

设想一下我们要访问一个银行账号:

type Account interface {
  Withdraw(uint)
  Deposit(uint)
  Balance() int
}

type Bank struct {
  account Account
}

func NewBank(account Account) *Bank {
  return &Bank{account: account}
}

func (bank *Bank) Withdraw(amount uint, actor_name string) {
  fmt.Println("[-]", amount, actor_name)
  bank.account.Withdraw(amount)
}

func (bank *Bank) Deposit(amount uint, actor_name string) {
  fmt.Println("[+]", amount, actor_name)
  bank.account.Deposit(amount)
}

func (bank *Bank) Balance() int {
  return bank.account.Balance()
}

因为 Account 是一个接口,所以我们提供一个简单的实现:


type SimpleAccount struct{
  balance int
}

func NewSimpleAccount(balance int) *SimpleAccount {
  return &SimpleAccount{balance: balance}
}

func (acc *SimpleAccount) Deposit(amount uint) {
  acc.setBalance(acc.balance + int(amount))
}

func (acc *SimpleAccount) Withdraw(amount uint) {
  if acc.balance >= int(mount) {
    acc.setBalance(acc.balance - int(amount))
  } else {
    panic("杰克穷死")
  }
}

func (acc *SimpleAccount) Balance() int {
  return acc.balance
}

func (acc *SimpleAccount) setBalance(balance int) {
  acc.add_some_latency()  //增加一个延时函数,方便演示
  acc.balance = balance
}

func (acc *SimpleAccount) add_some_latency() {
  <-time.After(time.Duration(rand.Intn(100)) * time.Millisecond)
}

你可能注意到了 balance 没有被直接修改,而是被放到了 setBalance 方法里进行修改。这样设计是为了更好的描述问题。稍后我会做出解释。

把上面所有部分弄好以后我们就可以像下面这样使用它啦:

func main() {
  balance := 80
  b := NewBank(bank.NewSimpleAccount(balance))
 
  fmt.Println("初始化余额", b.Balance())