一、协程
goroutine 协程
语法 go 函数 ,启动了一个子协程
父协程退出,子协程马上停止
1、简单粗暴 让父进程休眠时间,
var i = 0
func main() {
//启动四个协程
go XAdd()
go XAdd()
go XAdd()
go XAdd()
// 把这行注释后,i打印是0
time.Sleep(time.Second * 3) // 简单粗暴休眠3秒
fmt.Println(i)
}
func XAdd() { i++ }
2、使用协程计数器
全局区域
建立一个 var wg sync.WaitGroup
父协程中
设置等待3个子协程结束 wg.Add(3)
等待数字变成0 wg.Wait()
子协程中
数字减去1添加 wg.Done()
var wg sync.WaitGroup
var x = 0
func main() {
wg.Add(3)
go XAdd()
go XAdd()
go XAdd()
wg.Wait()
fmt.Println(x) //打印的x不一定是15000
}
func XAdd() {
for i := 0; i < 5000; i++ {
x = x + 1
}
wg.Done()
}
二、简单同步问题
1、简短互斥锁
在全局变量中
sync.Mutex
函数中
Lock():加锁,其他无法使用
Unlock():解锁,其他能使用
var wg sync.WaitGroup
var x = 0
func main() {
wg.Add(3)
go XAdd()
go XAdd()
go XAdd()
wg.Wait()
fmt.Println(x) //打印的x不一定是15000
}
var loc sync.Mutex //锁
func XAdd() {
for i := 0; i < 5000; i++ {
loc.Lock()
x = x + 1
loc.Unlock()
}
wg.Done()
}
2、读写锁
sync.RWMutex(读写互斥锁)
方法:
Lock():写锁,阻塞其他所有的读和写操作
Unlock():释放写锁。
RLock():读锁,允许多个 协程 同时持有读锁,但会阻塞写操作
RUnlock():释放读锁。
使用场景:适用于读操作频繁且写操作相对较少的场景
var wg sync.WaitGroup
var x = 0
func main() {
wg.Add(5)
go XAdd()
go XAdd()
go XRead()
go XRead()
go XRead()
wg.Wait()
fmt.Println(x) //打印的x不一定是15000
}
var loc sync.RWMutex //读写锁
func XAdd() {
for i := 0; i < 5000; i++ {
loc.Lock()
x = x + 1
loc.Unlock()
}
wg.Done()
}
func XRead() {
for i := 0; i < 4; i++ {
loc.RLock()
fmt.Println(x)
loc.RUnlock()
}
wg.Done()
}
3.原子操作(atomic包)
var wg sync.WaitGroup
var x int64 = 0
func main() {
wg.Add(3)
go XAdd()
go XAdd()
go XAdd()
wg.Wait()
fmt.Println(x)
}
func XAdd() {
for i := 0; i < 200000; i++ {
atomic.AddInt64(&x, 1) // 这里使用原子操作
}
wg.Done()
}
二、协程通信
chan通道,是队列
var wg sync.WaitGroup //是父进程等待子进程
func main() {
wg.Add(1)
// var ch1 chan int // 是引用类型 是个nil
ch := make(chan int) // 开通一个 无缓冲,的通道
go fu(ch) //要这个通道和子协程链接
ch <- 999 //才能向通道输入内容
hh := <-ch
fmt.Println(hh," am ii ")
wg.Wait()
}
func fu(ch chan int) {
ii := <-ch // 等待接受内容
fmt.Println(ii, " am ii ")
ch <- 66
close(ch) //用完了,记得关闭
wg.Done()
}
函数的参数中
chan<- 只能写
<-chan 只能读
func fu(ch chan int) // 可以写和读
func fu(ch <-chan int) //只能读
func fu(ch chan<- int) //只能写
有缓冲,的通道,容量为 1
ch := make(chan int, 1)
是一个队列,
队列满了,就不能写入
队列空,就不能读取
读取时
i, ok := <-ch
// ok是false,说明已经没有啦
// 会读取,不是副本的遍历
for i := range ch {
fmt.Println(i)
}
len(ch) 求已经存多少
cap(ch) 求容量大小
GO基础Day11、简单并发
于 2024-12-23 16:09:03 首次发布