课程名称:GO开发工程师
课程章节:6-5:传统同步机制;6-6/7:并发模式
课程讲师: ccmouse
课程内容:
- 传统同步机制
type atomicInt struct {
value int
lock sync.Mutex // 互斥锁
}
func (a *atomicInt) increment() {
fmt.Println("safe increment")
// 采用匿名函数对某一块代码区进行保护
// 这样defer只控制这个匿名函数,函数匿名退出即释放锁
func() {
a.lock.Lock()
defer a.lock.Unlock()
a.value++
}()
}
func (a *atomicInt) get() int {
a.lock.Lock()
defer a.lock.Unlock()
return int(a.value)
}
- 并发模式一:
对每个参数里的chan 开一个go routine
// 将c1和c2生成的数据发给c, 外部从c里取数据,c1或c2谁先发过来就处理谁
func fanIn(c1 chan string, c2 chan string) chan string {
c := make(chan string)
go func() {
for {
c <- <-c1 // 将c1里的值发给c
}
}()
go func() {
for {
c <- <-c2
}
}()
return c
}
- 并发模式二:
通过Select并发执行
func fanInSelect(c1 chan string, c2 chan string) chan string {
c := make(chan string)
// 只用一个go routine ,借助系统select能力实现对多个channel数据的等待接收
go func() {
for {
select {
case v := <-c1:
c <- v
case v := <-c2:
c <- v
}
}
}()
return c
}
- 并发陷阱:
func fanInFor(chs ...chan string) chan string {
c := make(chan string)
// ch 全局只有一份
for _, ch := range chs {
// 这样做的结果,会陷入循环陷阱
// 循环时创建go routine
// 真正读取ch值时,ch值已经不是当初创建go routine时的ch了(异步)
//go func() {
// for {
// c <- <-ch
// }
//}()
// 解决方案1:用一个变量接收
//chCopy := ch
//go func() {
// for {
// //c <- <-ch
// c <- <-chCopy
// }
//}()
// 解决方案2:传值
go func(ch chan string) {
for {
c <- <-ch
}
}(ch)
}
return c
}
课程收获:
对于确定数量的并发,可以使用select,只需要开一个go routine
不确定的可以使用for range对开多个go routine去处理,但要注意真正执行者传值的控制。