为了保护一个对象的并发修改,我们可以使用一个后台的协程来顺序执行一个匿名函数,而不是通过同步 互斥锁(Mutex) 进行锁定。

    在下面的程序中,我们有一个 Person 类型,它包含了一个匿名函数类型的通道字段 chF。它在构造器方法 NewPerson 中初始化,用一个协程启动一个 backend() 方法。这个方法在一个无限 for 循环中执行所有被放到 chF 上的函数,有效的序列化他们,从而提供安全的并发访问。改变和获取 salary 可以通过一个放在 chF 上的匿名函数来实现,backend() 会顺序执行它们。注意如何在 Salary 方法中的闭合(匿名)函数中去包含 fChan 通道。

    这是一个简化的例子,并且它不应该在这种情况下应用,但是它展示了如何在更复杂的情况下解决问题。

    示例 14.19—conc_access.go:

    1. package main
    2. import (
    3. "fmt"
    4. "strconv"
    5. )
    6. type Person struct {
    7. Name string
    8. salary float64
    9. chF chan func()
    10. }
    11. func NewPerson(name string, salary float64) *Person {
    12. p := &Person{name, salary, make(chan func())}
    13. go p.backend()
    14. return p
    15. }
    16. func (p *Person) backend() {
    17. for f := range p.chF {
    18. f()
    19. }
    20. }
    21. // 设置 salary.
    22. func (p *Person) SetSalary(sal float64) {
    23. p.chF <- func() { p.salary = sal }
    24. }
    25. // 取回 salary.
    26. func (p *Person) Salary() float64 {
    27. fChan := make(chan float64)
    28. p.chF <- func() { fChan <- p.salary }
    29. return <-fChan
    30. }
    31. func (p *Person) String() string {
    32. return "Person - name is: " + p.Name + " - salary is: " +
    33. strconv.FormatFloat(p.Salary(), 'f', 2, 64)
    34. }
    35. func main() {
    36. bs := NewPerson("Smith Bill", 2500.5)
    37. fmt.Println(bs)
    38. bs.SetSalary(4000.25)
    39. fmt.Println("Salary changed:")
    40. fmt.Println(bs)
    41. }

    /* 输出结果:

    1. Person - name is: Smith Bill - salary is: 2500.50
    2. Salary changed:
    3. Person - name is: Smith Bill - salary is: 4000.25

    */