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

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

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

现在账号被命名为 ConcurrentAccount,像下面这样来实现:

type ConcurrentAccount struct {
  account     *SimpleAccount
  deposits    chan uint
  withdrawals chan uint
  balances    chan chan int
}

func NewConcurrentAccount(amount int) *ConcurrentAccount{
  acc := &ConcurrentAccount{
    account :    &SimpleAccount{balance: amount},
    deposits:    make(chan uint),
    withdrawals: make(chan uint),
    balances:    make(chan chan int),
  }
  acc.listen()
 
  return acc
}

func (acc *ConcurrentAccount) Balance() int {
  ch := make(chan int)
  acc.balances <- ch
  return <-ch
}

func (acc *ConcurrentAccount) Deposit(amount uint) {
  acc.deposits <- amount
}

func (acc *ConcurrentAccount) Withdraw(amount uint) {
  acc.withdrawals <- amount
}

func (acc *ConcurrentAccount) listen() {
  go func() {
    for {
      select {
      case amnt := <-acc.deposits:
        acc.account.Deposit(amnt)
      case amnt := <-acc.withdrawals:
        acc.account.Withdraw(amnt)
      case ch := <-acc.balances:
        ch <- acc.account.Balance()
      }
    }
  }()
}

ConcurrentAccount 同样封装了 SimpleAccount ,然后增加了通信通道

调用代码和加锁版本的一样,这里就不写了,唯一不一样的就是初始化银行账号的时候:

b := NewBank(bank.NewConcurrentAccount(balance))

运行产生的结果和加锁版本一样:


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

让我们来深入了解一下细节。

通过通信来共享内存是如何工作的

一些基本注意点:

共享资源被封装在一个控制流程中。

结果就是资源成为了非共享状态。没有处理程序能够直接访问或者修改资源。你可以看到访问和修改资源的方法实际上并没有执行任何改变。

func (acc *ConcurrentAccount) Balance() int {
    ch := make(chan int)
    acc.balances <- ch
    balance := <-ch
    return balance
  }