Golang中多线程设计模式

生成器设计模式

生成器模式,产生一个可以不停生成数据的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func randNum(max int) <-chan int {
rand.Seed(time.Now().UnixNano())
ch := make(chan int)
go func() {
for {
select {
case ch <- rand.Intn(max):
}
}
}()
return ch
}

func main() {
re := randNum(100)
for i := 0; i < 100; i++ {
fmt.Println(<-re)
}
}

过滤器

例如获取范围内的素数,可以通过最小的数字作为过滤,将后续能整除的数字过滤掉,当队列为空,则结束

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
func filer(ch chan int, wg *sync.WaitGroup) {
wg.Add(1)
defer wg.Done()
num, ok := <-ch // 第一个数是最小的数,这个数字肯定是质数
if !ok {
return
}
fmt.Println(num) // 将质数打印出来
chlid := make(chan int) // 创建本质数的channel
defer close(child) // 结束channel,防止goroutine泄露
go filer(chlid, wg) // 嵌套
for i := range ch {
if i%num != 0 { // 排除能整除最小的数
chlid <- i // 将过滤后的数字丢到channel中,在下一个channel中过滤
}
}
}

func main() {
n := 10000
ch := make(chan int)
wg := &sync.WaitGroup{}
go func() {
for i := 2; i <= n; i++ {
ch <- i
}
close(ch)
}()
filer(ch, wg)
wg.Wait() // 等待所有的数字全部都被过滤。
}

单例模式

单例模式是指在多线程下执行同一个命令,最终只会产生执行一次的效果。常用于初始化。

golang中的单例模式一般通过sync.Once实现,init函数也可以实现,这两者其实都是通过一个标记实现是否已经进行单例的初始化。

例如使用sync.Once

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var money int
var one sync.Once

func main() {
wg := sync.WaitGroup{}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
one.Do(func() {
time.Sleep(time.Millisecond)
money += 10
})
}()
}
wg.Wait()
fmt.Println(money)
}

使用标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var money int
var one bool
var mu sync.Mutex

func main() {
wg := sync.WaitGroup{}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
defer mu.Unlock()
if !one {
time.Sleep(time.Millisecond)
money += 10
one = true
}
}()
}
wg.Wait()
fmt.Println(money)
}

判断是否已经执行的时候,可以不需要加锁,后续修改值的时候再加,可以节省资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var money int
var one bool
var mu sync.Mutex

func main() {
wg := sync.WaitGroup{}
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if !one {
mu.Lock()
defer mu.Unlock()
if !one { // 后续加锁之后,还需要再判断一次,以防止有些线程经过了第一层判断
time.Sleep(time.Millisecond)
money += 10
one = true
}
}
}()
}
wg.Wait()
fmt.Println(money)
}