一、除了使用通道channel来实现同步外,还可以通过Mutex互斥锁的方式来实现。
当多个资源来访问资源时,要实现程序的原子性。 如果没有加锁来实现以下程序。
加入WaitGroup还是无法实现程序的原子性
var i int = 100
var wg sync.WaitGroup
func add(){
defer wg.Done()
i += 1
fmt.Printf("i++ : %v\n", i)
}
func sub(){
defer wg.Done()
i -= 1
fmt.Printf("i-- : %v\n", i)
}
func main(){
for i:=0; i<100; i++ {
wg.Add(1)
go add() // 子协程1
wg.Add(1)
go sub() // 子协程2
}
fmt.Printf("最后i的值为: %v\n", i)
}
而加入互斥锁后,可以保证程序的原子性执行
var lock sync.Mutex
在代码函数的前后分别加上 lock.Lock() 加锁操作, lock.Unlock() 释放锁
二、 并发编程select switch
- select 是 Go语言中的一个控制结构,类似于switch ,用于处理异步IO操作,select会监听case语句中的channel的读写操作,当case中channel 读写操作为非阻塞情况下,将会触发相应的操作。
select中case语句必须是一个channle操作
select中的default子句总是可运行的
- 如果有多个case语句可以执行,select会随机公平的选出一个执行,其他不会执行
- 如果没有可运行的case语句,且有default语句,那么最后会执行default语句
- 如果没有可运行的case语句,且没有default语句,select将会被阻塞,直到某个case通信可以执行
func main(){
var chanInt = make(chan int)
var chanString = make(chan string)
go func(){
chanInt <- 100
chanString <- "hello"
close(chanInt)
close(chanString)
}()
for{
select{
case r:= <- chanInt:
fmt.Printf("chanInt: %v\n", r)
case r:= <- chanString:
fmt.Printf("chanString: %v\n", r)
default:
fmt.Printf("end......")
}
time.Sleep(time.Second)
}
}
三、 并发编程Timer / Ticker
Timer实现定时器的功能,只能执行一次,内部主要通过通道channel来实现。
Ticker可以周期的执行 , 方法创建和运用和Timer相同,只是Ticker可以通过循环来周期执行,使用ticker.Stop()来停止。
Timer(两种)创建方法:
time.NewTimer()
time.After()
fmt.Printf("time.Now() : %v\n", time.Now())
// (1)NewTimer
timer := time.NewTimer(time.Second * 2) //定时2秒后执行
t1 := <-timer.C // .C 阻塞的直到时间到达
fmt.Printf("t1 : %v\n", t1)
// 最后相互差两秒
// (2)也可以直接等待
<-timer.C // == time.Sleep(time.Second * 2)
// (3)或者 After
<-time.After(time.Second * 2)
四、 并发编程原子操作
atomic.AddInt64(&i, 1) // +1 操作
atomic.AddInt64(&i, 1) // -1 操作
atomic.LoadInt64(&i) // 读i操作
atomic.StoreInt64(&j, 100) // 写操作