go协程同步的三种方式
1.Mutex 2.channel 3.WaitGroup
1.使用锁的方式
1 package main 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 ) 8 9 var share_cnt uint64 = 0 10 11 var lck sync.Mutex 12 13 func incrShareCnt() { 14 for i := 0; i < 1000000; i++ { 15 lck.Lock() 16 share_cnt++ //共有资源 17 lck.Unlock() 18 } 19 fmt.Println(share_cnt) 20 } 21 func main() { 22 for i := 0; i < 2; i++ { 23 go incrShareCnt() 24 } 25 time.Sleep(1 * time.Second) //1秒 26 }
2.使用channel通道可以用来作为消息机制也可作为锁机制
1.有缓冲/无缓冲channel
无缓冲的channel只能发送完之后协程立马阻塞,只有等有协程接受了之后才能继续发送,
等待协程接受了,之后立马阻塞,等待channel中有数据才会启动
1 package main 2 3 import ( 4 "fmt" 5 "time" 6 ) 7 8 func main() { 9 c := make(chan int) //声明一个int类型的无缓冲通道 10 go func() { 11 fmt.Println("准备发送1数据") 12 c <- 1 13 fmt.Println("发送1数据完毕") 14 fmt.Println("协程开始睡眠1秒") 15 time.Sleep(time.Second) 16 fmt.Println("协程结束睡眠") 17 c <- 2 18 fmt.Println("发送2数据完毕") 19 }() 20 21 fmt.Println("主线程休眠1秒") 22 time.Sleep(time.Second) 23 fmt.Println("主线程结束休眠") 24 i := <-c 25 fmt.Printf("接受 %d\n", i) 26 i = <-c 27 fmt.Printf("接受 %d\n", i) 28 time.Sleep(time.Second) 29 }
1//运行结果
主线程休眠1秒 2 准备发送1数据 3 主线程结束休眠 4 发送1数据完毕 5 协程开始睡眠1秒 6 接受 1 7 协程结束睡眠 8 发送2数据完毕 9 接受
注意:
func main() {
c := make(chan int) //声明一个int类型的无缓冲通道
c <- 1
i := <- c
fmt.Printf("receive %d\n", i)
}会报错
在同一个线程里进行channel读写操作会发生死锁
有缓冲的channel阻塞条件不同:当缓冲区满了发送线程阻塞,缓冲区空了接受线程阻塞
1 package main 2 3 import ( 4 "fmt" 5 "time" 6 ) 7 8 func main() { 9 c := make(chan int, 2) //声明一个int类型的有缓冲通道 10 go func() { 11 for i := 0; i < 4; i++ { 12 c <- i 13 fmt.Printf("send %d\n", i) 14 } 15 time.Sleep(5 * time.Second) 16 for i := 4; i < 6; i++ { 17 c <- i 18 fmt.Printf("send %d\n", i) 19 } 20 }() 21 22 for i := 0; i < 6; i++ { 23 time.Sleep(time.Second) 24 fmt.Printf("receive %d\n", <-c) 25 } 26 }
send 0
send 1
receive 0
send 2
receive 1
send 3
receive 2
receive 3
send 4
send 5
receive 4
receive 5
3.WaitGroup函数的使用
1 package main 2 import ( 3 "fmt" 4 "sync" 5 ) 6 func main() { 7 fmt.Println("Hello World") 8 var waitgroup sync.WaitGroup 9 waitgroup.Add(1) 10 go myFunc(&waitgroup) 11 waitgroup.Wait() 12 fmt.Println("Finished Execution") 13 } 14 func myFunc(waitgroup *sync.WaitGroup) { 15 fmt.Println("Inside my goroutine") 16 waitgroup.Done() 17 }
上面代码输出结果为:
Hello World
Inside my goroutine
Finished Execution
达到了我们想要的结果。
接下来对上面的代码做一些解释:
我们在开启协程之前,先调用了WaitGroup的Add(1)方法,是要设置主函数需要等待完成的协程数为1,Wait()方法是等待协程的完成。我们在协程中调用的WaitGroup的Done()方法,意思是当前协程执行完成(Done()做的工作其实就是把需要等待的协程个数减1),当需要等待的协程数为0时,则不需要再等待,继续执行以下的代码。
静,静,静