并发的特点:多个任务作用在一个cpu,从微观的角度看一个时间点其实只有一个任务在执行
并行的特点:多个任务作用在多个cpu,从微观的角度看,在一个时间点上多个任务同时执行,这样看来并行的速度更快
Go的协程:main.exe可以看醉哦是线程,也可以理解成进程,go可以轻松运行上十万个协程goroutine,(轻量级的线程),java这种语言千机的线程就可以耗光CPU了
协程资源竞争问题使用sync lock的是低水平的程序员,高水平的使用channel的
go build -race xxx.go可以看不到详细的输出,有没有资源竞争
clannel 管道,其实就是一个数据类型,可以理解成队列FIFO先进先出,first in first out
channel是线程安全的(因为它其实是一个队列)多个协程同时操作一个线程是不会发生资源竞争问题的,所以它是线程安全的
go 并发入门案例(幼儿园考试篇)goroutine +channel
package main
import (
"fmt"
)
func writeData(wChan chan int,num int){
for i:=0;i<num;i++{
wChan<-i//把i写入管道
if num%1000==0{
fmt.Println("写入到管道的值:",i)
}
}
close(wChan)//写完关闭管道,写完必须关闭哦,不关闭下面遍历会出错的
}
func readData(myWriteintChan chan int,exit chan bool){
for v:=range myWriteintChan{
if v%1000==0{
fmt.Println("读取到管道的值:",v)
}
}
exit<-true//读取完管道,标记可以关闭管道
}
func main(){
myNum := 99999
myWriteintChan := make(chan int,myNum)
//这里也可以用变量,不一定要管道,用变量的话readData返回个读取完毕的值,
//for阻塞的地方就判断返回值
existChan := make(chan bool,1)
go writeData(myWriteintChan,myNum)
go readData(myWriteintChan,existChan)
existFlag := <-existChan
//for阻塞起来,直到读取完毕就退出程序
for{
if existFlag==true{
break
}
}
}
go 并发入门案例二(小学毕业篇) goroutine +channel
1.开启一个协程,把1-1000的数据写入到管道
2.开启十个协程,从管道中读取数据,把读取到的数据写入到结果集管道
3.遍历结果集管道,计算1-1000相加的结果,输出结果
注意事项:1.使用完毕的管道记得关闭,在管道遍历之前需要关闭管道
结果:1-1000相加的结果是:500500
package main
import (
"fmt"
)
func inChan(inchannel chan int,num int){
for i:=1;i<=num;i++{
inchannel <- i//把i写入管道中
}
close(inchannel)//写入完毕,关闭管道
}
//读取管道的内容,把读取到的内容放入到
func readChan(readChannel chan int,resChan chan int,exitChan chan bool){
//每个协程进入这都阻塞读取管道的内容,直到管道没数据了,就退出
for{
getChannelValue,ok := <-readChannel //读取管道的内容
if !ok{//判断管道的读取内容是否为真
fmt.Println("readChan退出读取:",getChannelValue,ok)
//这里不能关闭管道哦,因为你取不到结果,可能别的协程能取到结果
exitChan <- true
break
}
resChan<-getChannelValue//数据读取成功,把从管道读取的数据放到结果管道
}
}
func main(){
//启动一个协程把1-1000的数据放到管道中
//启动10个协程消费管道里面的结果集,把获取到的结果放到新的管道里面(注意需等待所有协程执行完毕)
//创建一条管道并make定义管道数据类型并分配空间
yourNum := 1000
groutine := 10
numChan := make(chan int,yourNum)//1-1000写入到这个管道
resChan := make(chan int,yourNum)//读取到的数据放到这个管道
exitChan := make(chan bool,10) //关闭管道标记
resultNum:=0 //计算结果
//启动一个协程,往管道写入数据
go inChan(numChan,yourNum)
//启动10个进程取读取管道的内容
for i:=1;i<=groutine;i++{
go readChan(numChan,resChan,exitChan)
}
//把结果集的管道阻塞起来,直到结果集全部拿到,然后关闭管道
exitFlag,ok := <-exitChan
fmt.Println("exitChan:",exitFlag,ok)
if exitFlag==true {
close(resChan)//关闭结果集管道,下面才能遍历管道
}
//这个exitChan不能直接关闭,因为有十个协程同时在,取最后一个数据的时候,其他9个可能没有退出的
//直接关闭了的话会导致后面9个写入管道时候会报错
defer close(exitChan)
for chanValue:=range resChan{
resultNum = resultNum+chanValue
fmt.Print(" ",chanValue)
}
fmt.Printf("\n 1-%v相加的结果是:%v",yourNum,resultNum)
//1-1000相加的结果是:500500
}
开发中用到waitGroup需要注意死锁问题:go wait group dead 死锁问题
并发时候保证公共数据安全问题加锁问题注意事项:https://blog.csdn.net/qq_27517377/article/details/114824259