10.Go-goroutine,waitgroup,互斥锁,channel和select
程序员文章站
2023-11-28 18:48:52
10.1.goroutine goroutine的使用 10.2.waitgroup WaitGroup直译为等待组,其实就是计数器,只要计数器中有内容将一直阻塞 WaitGroup有三种方法 Add(delta int)表示向内部计数器添加增量(delta),其中参数delta可以使负数 Done ......
10.1.goroutine
goroutine的使用
//learn_go/main.go
package main
import (
"fmt"
"time"
)
func demo(count int) {
for i :=1; i < 10; i++{
fmt.println(count,":",i)
}
}
func main() {
for i :=1; i < 10; i++{
go demo(i)
}
//添加休眠时间等待goroutine执行结束
time.sleep(3e9)
}
10.2.waitgroup
waitgroup直译为等待组,其实就是计数器,只要计数器中有内容将一直阻塞
waitgroup有三种方法
- add(delta int)表示向内部计数器添加增量(delta),其中参数delta可以使负数
- done() 表示减少waitgroup计数器的值,应当在程序最后执行,相当于add(-1)
- wait() 表示阻塞知道waitgroup计数器为0
//learn_go/main.go
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.waitgroup
wg.add(5)
for i := 0; i < 5; i++{
go func(j int) {
fmt.println("第",j,"次执行")
wg.done()
}(i)
}
wg.wait()
fmt.println("程序结束")
}
10.3.互斥锁和读写锁
(1)互斥锁
可以使用sync.mutex对内容加锁,互斥锁的使用场景
- 多个gouroutine访问同一个函数代码段
- 操作一个全局变量
- 为了保证共享变量安全性,值安全性
(2)读写锁
go语言中的map不是线程安全的,多个gouroutine同时操作会出现错误
rwmutex可以添加多个读锁或者一个写锁,读写锁不能同时存在
map在并发下读写就需要结合读写锁完成
互斥锁表示锁的代码同一时间只能有一个goroutine运行,而读写锁表示在锁范围内数据的读写操作
//learn_go/main.go
package main
import (
"fmt"
"sync"
)
func main() {
var rwm sync.rwmutex
var wg sync.waitgroup
wg.add(10)
m := make(map[int]int)
for i := 0; i < 10; i++{
go func(j int) {
rwm.lock()
m[j] = j
fmt.println(m)
rwm.unlock()
wg.done()
}(i)
}
wg.wait()
fmt.println("程序结束")
}
10.4.channel
channel是进程内通信方式,每个channel只能传递一个类型的值,这个类型需要在声明channel时指定
channel在go中主要的两个作用:同步和通信
(1)声明channel的语法
- var 名称 chan 类型
- var 名称 chan <- 类型 只写
- var 名称 <- chan 类型 只读
- 名称 := make(chan int) 无缓存chanel
- 名称 := make(chan int) 无缓存channel
- 名称 := make(chan int,100) 有缓存channel
(2)操作channel的语法
- ch <- 值 向ch中添加一个值
- <- ch 从ch中取出一个值
- a := <-ch 从ch中取出一个值并赋值给a
- a,b := <-ch 从ch中取出一个值赋值给a,如果ch已经关闭或ch中没有值,b为false,
(3)无论是向channel存数据还是取数据都会阻塞
//learn_go/main.go
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
fmt.println("执行")
ch <- 111
}()
a := <- ch
fmt.println(a)
fmt.println("程序结束")
}
(4)使用channel实现gouroutine之间通信
//learn_go/main.go
package main
import "fmt"
func main() {
ch1 := make(chan string)
ch2 := make(chan int)
go func() {
ch1 <- "derek"
ch2 <- 111
}()
go func() {
content := <- ch1
fmt.println("取出数据:",content) //取出数据: derek
ch2 <- 222
}()
a := <- ch2
b := <- ch2
fmt.println(a,b) //111 222
fmt.println("程序结束")
}
(5)可以使用for range获取channel中内容
//learn_go/main.go
package main
import "fmt"
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
for i := 0; i<10;i++{
ch1 <- i
}
ch2 <- 222
}()
go func() {
for n := range ch1{
fmt.println(n)
}
}()
<- ch2
fmt.println("程序结束")
}
10.5.select
select执行过程
- 每个case必须是一个io操作
- 哪个case可以执行就执行哪个
- 所有case都不能执行时,执行default
- 所有case都不能执行且没有default,将会阻塞
//learn_go/main.go
package main
import "fmt"
func main() {
ch1 := make(chan int,2) //有缓存的channel
ch2 := make(chan int,3)
ch1 <- 111
ch2 <- 222
select {
case a := <-ch1:
fmt.println(a)
case b := <-ch2:
fmt.println(b)
default:
fmt.println("错误")
}
}
select多和for循环结合使用
//learn_go/main.go
package main
import "fmt"
func main() {
ch := make(chan int)
for i := 0; i < 10;i++{
go func(j int) {
ch <- j
}(i)
}
//用for循环一直接受
for {
select {
case a := <- ch:
fmt.println(a)
default:
}
}
}
下一篇: java类和对象详解