/
account.go
124 lines (106 loc) · 2.25 KB
/
account.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package bank
import (
"context"
"golang.org/x/sync/semaphore"
"sync"
)
// AccountV1 组合了Account,通过锁,保证线程安全
type AccountV1 struct {
Account
lock sync.Mutex
}
var _ Accountable = &AccountV1{}
func (a *AccountV1) Transfer(to Accountable, amount int64) {
too, ok := to.(*AccountV1)
if !ok {
return
}
left, right := a, too
if left.Id > right.Id {
left, right = too, a
}
left.lock.Lock()
defer left.lock.Unlock()
right.lock.Lock()
defer right.lock.Unlock()
a.transfer(to, amount)
}
// AccountV2 组合了Account,通过借助外部的Allocator,保证线程安全
type AccountV2 struct {
Account
}
var _ Accountable = &AccountV2{}
func (a *AccountV2) Transfer(to Accountable, amount int64) {
al := getAllocator()
al.Apply(a, to)
a.transfer(to, amount)
al.Free(a, to)
}
var allocator *Allocator
var allocatorOnce sync.Once
type Allocator struct {
occupied map[any]bool
mu *sync.Mutex
cond *sync.Cond
}
func (a *Allocator) Apply(from, to any) {
a.mu.Lock()
defer a.mu.Unlock()
for a.occupied[from] || a.occupied[to] {
a.cond.Wait()
}
a.occupied[from] = true
a.occupied[to] = true
}
func (a *Allocator) Free(from, to any) {
a.mu.Lock()
defer a.mu.Unlock()
delete(a.occupied, from)
delete(a.occupied, to)
a.cond.Broadcast()
}
func getAllocator() *Allocator {
allocatorOnce.Do(func() {
mu := &sync.Mutex{}
allocator = &Allocator{
occupied: make(map[any]bool, 1000),
mu: mu,
cond: sync.NewCond(mu),
}
})
return allocator
}
// AccountV3 组合了Account,通过信号量,保证线程安全
type AccountV3 struct {
Account
sema *semaphore.Weighted
}
func NewAccountV3(id int64, balance int64) *AccountV3 {
return &AccountV3{
Account: Account{
Id: id,
Balance: balance,
},
sema: semaphore.NewWeighted(1),
}
}
var _ Accountable = &AccountV3{}
func (a *AccountV3) Transfer(to Accountable, amount int64) {
too, ok := to.(*AccountV3)
if !ok {
return
}
left, right := a, too
if left.Id > right.Id {
left, right = too, a
}
if err := left.sema.Acquire(context.Background(), 1); err != nil {
return
}
defer left.sema.Release(1)
if err := right.sema.Acquire(context.Background(), 1); err != nil {
return
}
defer right.sema.Release(1)
a.transfer(to, amount)
}