摄影网站设计说明,傻瓜式装修设计软件,西安网站建设加q479185700,万由nas做网站目录
一、并发安全性
1.1 变量并发安全性
1.2 容器并发安全性
二、多路复用
三、协程常见的面试题
3.1交替打印奇数偶数 一、并发安全性
1.1 变量并发安全性
这个和C中并发安全是一样的#xff0c;主要是多个线程对临界资源的同时访问#xff0c;最经典的就是 n操作…目录
一、并发安全性
1.1 变量并发安全性
1.2 容器并发安全性
二、多路复用
三、协程常见的面试题
3.1交替打印奇数偶数 一、并发安全性
1.1 变量并发安全性
这个和C中并发安全是一样的主要是多个线程对临界资源的同时访问最经典的就是 n操作因为这一步并不是原子操作的。
这里我们可以用到 atomic原子操作让n变成一步也可也使用加锁的办法
我们先来模拟一下错误的操作这里我们开1000个协程都执行 n的操作
package mainimport (fmtsync
)var n int //nfunc main() {wg : sync.WaitGroup{}wg.Add(1000)for i : 0; i 1000; i {go func() {defer wg.Done()n}()}wg.Wait()fmt.Println(n)
}
我们运行一下 明显可以看到结果不是1000这就是并发不安全的
1. 使用atomic 2. 用读写锁实现 1.2 容器并发安全性 读写一般的map是不可以的我们需要用到sync.Map , 存数据是 mp.store( i , i ), 读数据是 mp.lode(i) 二、多路复用
我们都知道当前的IO多路复用有三种 select 、poll、epoll 在go语言当中只有select这里是针对不同的操作系统进行了封装我们也不用考虑到底是什么只管用就行了
go的select在遍历的时候是只要有一个准备好了就会返回 // 倒计时 模拟火箭发射
func countDown(countCh chan int, n int, finishCh chan struct{}) {if n 0 { //从n开始倒数return}for {countCh - n //把n放入管道time.sleep(time.second) //等待一秒n-- //n减1if n 0 { //n减到0时退出finishCh - struct{}{} //成功结束break //退出for循环}}
}// 中止 键盘有输入就表示要中断
func abort(ch chan struct{}) {buffer : make([]byte, 1)os.Stdin.Read(buffer) //阻塞式IO如果标准输入里没数据该行一直阻塞。注意在键盘上敲完后要按下Enter才会把输入发给Stdinch - struct{}{}
}func main() {countCh : make(chan int)finishCh : make(chan struct{})go countDown(countCh, 10, finishCh) //开一个子协程去往countCh和finishCh里放数据abortCh : make(chan struct{})go abort(abortCh) //开一个子协程去往abortCh里放数据LOOP:for { //循环监听select { //同时监听3个channel谁先准备好就执行谁然后进入下一次for循环case n : -countCh:fmt.Println(n)case -finishCh:fmt.Println(finish)break LOOP //退出for循环。在使用for select时单独一个break不能退出for循环case -abortCh:fmt.Println(abort)break LOOP //退出for循环}}
}
三、协程常见的面试题
3.1交替打印奇数偶数
方法一使用无缓存的channel 进行通信
// PrintOddAndEven1 /*
func PrintOddAndEven1() {//方法一,使用无缓冲的channel进行通信var wg new(sync.WaitGroup) //注意这里需要是指针go语言当中都是值传递wg.Add(2)ch : make(chan struct{}) //无缓冲channeldefer close(ch)maxVal : 100go func() {defer wg.Done()for i : 1; i maxVal; i {ch - struct{}{}if i%2 1 { //奇数fmt.Printf(the odd is %d\n, i)}}}()go func() {defer wg.Done()for i : 1; i maxVal; i {-ch //从管道当中读取一个数据if i%2 0 { //偶数fmt.Printf(the even is %d\n, i)}}}()wg.Wait()}
func main() {PrintOddAndEven1()fmt.Println(over)
}/*
原理因为c1是无缓存的所以只有当读写同时就绪才不会被阻塞当同时就绪的时候两个协程会同时进入if条件语句他们的i值都是一样的这时候只有一个是满足条件的所以会按顺序交替打印出 1~100.
方法二使用有缓存的channel进行通信
// 方法二用有缓存的channel进行通信
func HaveCach() {wg : sync.WaitGroup{}wg.Add(2)c1 : make(chan int, 1)c2 : make(chan int, 1)defer close(c1)defer close(c2)c1 - 1 //先往c1当中写入一个数据确保先打印奇数go func() {defer wg.Done()for i : 1; i 50; i 2 {-c1fmt.Printf(奇数 %d\n, i)c2 - 1 //通知c2}}()go func() {defer wg.Done()for i : 2; i 50; i 2 {-c2fmt.Printf(偶数 %d\n, i)c1 - 1 //通知c1}}()wg.Wait()
}func main() {HaveCach()fmt.Println(over)
}
第二个方法使用这个有缓冲的channel。有缓冲的channel当容量没有达到上限时写入不会阻塞在这里奇数协程的channel容量为1我们提前给他写入了一个数据因此当偶数和奇数协程都开始读取数据时首先读取到数据的是奇数协程奇数协程打印完之后在通知偶数协程打印偶数协程打印完成之后在通知奇数协程重复下去就实现了交替打印的效果。