目录
Golang-并发编程
并行和并发 go语言并发优势 goroutine是什么 创建goroutine runtime包 channel是什么 channel的基本使用 无缓冲的channel 有缓冲的channel close() 单方向的channel 定时器 select 携程同步锁只需要在语句前添加go关键字,就可以创建并发执行单元
packagemain
import(
"fmt"
"time"
)//测试协程
//循环打印内容
funcnewTask(){
i:=0
for{
i++
fmt.Printf("newgoroutine:i=%d\n",i)
time.Sleep(1*time.Second)
}
}//main()相当于是主协程
funcmain(){
//启动子协程
gonewTask()
i:=0
for{
i++
fmt.Printf("maingoroutine:i=%d\n",i)
time.Sleep(1*time.Second)
}
}开发⼈员无需了解任何执⾏细节,调度器会自动将其安排到合适的系统线程上执行
如果主协程退出了,其他任务还执行吗?不执行
packagemain import( "fmt" "time" ) //main()相当于是主协程 funcmain(){ //匿名子协程 gofunc(){ i:=0 for{ i++ fmt.Println("子协程i=",i) time.Sleep(1*time.Second) } }() i:=0 for{ i++ fmt.Println("主协程i=",i) time.Sleep(1*time.Second) //主协程第二次后退出 ifi==2{ break } } }程序没任何输出,也不报错
packagemain import( "fmt" "time" ) //main()相当于是主协程 funcmain(){ //匿名子协程 gofunc(){ i:=0 for{ i++ fmt.Println("子协程i=",i) time.Sleep(1*time.Second) } }() }runtime.Gosched():用于让出CPU时间片,调度器重新安排任务调度,还是有几率分配到它的
packagemain import( "fmt" "runtime" ) funcmain(){ //匿名子协程 gofunc(sstring){ fori:=0;i<2;i++{ fmt.Println(s) } }("world") //主协程 fori:=0;i<2;i++{ runtime.Gosched() fmt.Println("hello") } }runtime.Goexit():立即终止当前协程
packagemain import( "fmt" "time" "runtime" ) funcmain(){ //匿名子协程 gofunc(){ deferfmt.Println("A.defer") //匿名函数 func(){ deferfmt.Println("B.defer") //此时只有defer执行 runtime.Goexit() fmt.Println("B") }() fmt.Println("A") }() for{ time.Sleep(time.Second) } }runtime.GOMAXPROCS():设置并行计算的CPU核数,返回之前的值
packagemain import( "runtime" "fmt" ) funcmain(){ n:=runtime.GOMAXPROCS(3) fmt.Println("n=%d\n",n) //循环执行2个 for{ gofmt.Print(0) fmt.Print(1) } }channel可以用内置make()函数创建
定义一个channel时,也需要定义发送到channel的值的类型
make(chan类型)//无缓冲的通道 make(chan类型,容量)//有缓冲的通道当capacity=0时,channel是无缓冲阻塞读写的,当capacity>0时,channel有缓冲、是非阻塞的,直到写满capacity个元素才阻塞写入
channel通过操作符<-来接收和发送数据,发送和接收数据语法:
channel<-value//发送value到channel <-channel//接收通道数据,并丢弃 x:=<-channel//通道取值并赋给x x,ok:=<-channel//ok是检查通道是否关闭或者是否为空channel基本使用
packagemain import"fmt" funcmain(){ //创建存放int类型的通道 c:=make(chanint) //子协程 gofunc(){ deferfmt.Println("子协程结束") fmt.Println("子协程正在运行...") //将666发送到通道c c<-666 }() //若已取出数据,下面再取会报错 //<-c //主协程取数据 //从c中取数据 num:=<-c fmt.Println("num=",num) fmt.Println("主协程结束") }发送者->(通道(有可能有数据阻塞))->接受者
packagemain import( "fmt" "time" ) funcmain(){ //创建无缓冲通道 c:=make(chanint,0) //长度和容量 fmt.Printf("len(c)=%d,cap(c)=%d\n",len(c),cap(c)) //子协程存数据 gofunc(){ deferfmt.Println("子协程结束") //向通道添加数据 fori:=0;i<3;i++{ c<-i fmt.Printf("子协程正在运行[%d]:len(c)=%d,cap(c)=%d\n",i,len(c),cap(c)) } }() time.Sleep(2*time.Second) //主协程取数据 fori:=0;i<3;i++{ num:=<-c fmt.Println("num=",num) } fmt.Println("主协程结束") }发送者->(通道(数据),(数据)(...))->接受者
上面代码创建时修改容量即可
//创建有缓存的通道 c:=make(chanint,3)可以通过内置的close()函数关闭channel
packagemain import"fmt" funcmain(){ //创建通道 c:=make(chanint) //子协程存数据 gofunc(){ fori:=0;i<5;i++{ c<-i } //子协程close close(c) }() //主协程取数据 for{ ifdata,ok:=<-c;ok{ fmt.Println(data) }else{ break } } fmt.Println("Finshed") }也可以如下遍历
fordata:=rangec{ fnt.Println(data) }默认情况下,通道是双向的,也就是,既可以往里面发送数据也可以接收数据
go可以定义单方向的通道,也就是只发送数据或者只接收数据,声明如下
varch1chanint//正常的
varch2chan<-float64//单向的,只用于写float64的数据
varch3<-chanint//单向的,只用于读取int数据可以将channel隐式转换为单向队列,只收或只发,不能将单向channel转换为普通channel
funcmain(){
//创建通道
c:=make(chanint,3) //1.将c准换为只写的通道 varsend<-chanint=c //2.将c转为只读的通道 varrecv<-chanint=c //往send里面写数据 send<-1 //从recv读数据 <-recv}
单方向的channel有什么用?模拟生产者和消费者
packagemain import"fmt" //生产者,只写 funcproducter(outchan<-int){ //关闭资源 deferclose(out) fori:=0;i<5;i++{ out<-i } } //消费者,只读 funcconsumer(in<-chanint){ fornum:=rangein{ fmt.Println(num) } } funcmain(){ //创建通道 c:=make(chanint) //生产者运行,向管道c存数据 goproducter(c) //消费者运行 consumer(c) fmt.Println("done") }Timer:定时,时间到了响应一次
packagemain import( "time" "fmt" ) funcmain(){ //1.基本使用 //创建定时器 //2秒后,定时器会将一个时间类型值,保存向自己的c //timer1:=time.NewTimer(2*time.Second) ////打印当前时间 //t1:=time.Now() //fmt.Printf("t1:%v\n",t1) ////从管道中取出C打印 //t2:=<-timer1.C //fmt.Printf("t2:%v\n",t2) //2.Timer只响应一次 //timer2:=time.NewTimer(time.Second) //for{ //<-timer2.C //fmt.Println("时间到") //} //3.通过Timer实现延时的功能 ////(1)睡眠 //time.Sleep(2*time.Second) //fmt.Println("2秒时间到") ////(2)通过定时器 //timer3:=time.NewTimer(2*time.Second) //<-timer3.C //fmt.Println("2秒时间到") ////(3)After() //<-time.After(2*time.Second) //fmt.Println("2秒时间到") //4.停止定时器 //timer4:=time.NewTimer(3*time.Second) ////子协程 //gofunc(){ //<-timer4.C //fmt.Println("定时器器时间到,可以打印了") //}() //stop:=timer4.Stop() //ifstop{ //fmt.Println("timer4已关闭") //} //5.重置定时器 timer5:=time.NewTimer(3*time.Second) //定时器改为1秒 timer5.Reset(1*time.Second) fmt.Println(time.Now()) fmt.Println(<-timer5.C) for{ } }Ticker:响应多次
packagemain
import(
"time"
"fmt"
)funcmain(){
//创建定时器,间隔1秒
ticker:=time.NewTicker(time.Second) i:=0 //子协程 gofunc(){ for{ <-ticker.C fmt.Println(<-ticker.C) i++ fmt.Println("i=",i) //停止定时器 ifi==5{ ticker.Stop() } } }() //死循环 for{ }}
go语言提供了select关键字,可以监听channel上的数据流动
语法与switch类似,区别是select要求每个case语句里必须是一个IO操作
select{ case<-chan1: //如果chan1成功读到数据,则进行该case处理语句 casechan2<-1: //如果成功向chan2写入数据,则进行该case处理语句 default: //如果上面都没有成功,则进入default处理流程 } packagemain import( "fmt" ) funcmain(){ //创建数据通道 int_chan:=make(chanint,1) string_chan:=make(chanstring,1) //创建2个子协程,写数据 gofunc(){ //time.Sleep(2*time.Second) int_chan<-1 }() gofunc(){ string_chan<-"hello" }() //如果都能匹配到,则随机选择一个去跑 select{ casevalue:=<-int_chan: fmt.Println("intValue:",value) casevalue:=<-string_chan: fmt.Println("strValue:",value) } fmt.Println("finish") }go中channel实现了同步,确保并发安全,同时也提供了锁的操作方式
go中sync包提供了锁相关的支持
Mutex:以加锁方式解决并发安全问题
packagemain import( "time" "fmt" "sync" ) //账户 typeAccountstruct{ moneyint flagsync.Mutex } //模拟银行检测 func(a*Account)Check(){ time.Sleep(time.Second) } //设置账户余额 func(a*Account)SetAccount(nint){ a.money+=n } //查询账户余额 func(a*Account)GetAccount()int{ returna.money } //买东西1 func(a*Account)Buy1(nint){ a.flag.Lock() ifa.money>n{ //银行检测 a.Check() a.money-=n } a.flag.Unlock() } //买东西2 func(a*Account)Buy2(nint){ a.flag.Lock() ifa.money>n{ //银行检测 a.Check() a.money-=n } a.flag.Unlock() } funcmain(){ varaccountAccount //设置账户余额 account.SetAccount(10) //2个子协程买东西 goaccount.Buy1(6) goaccount.Buy2(5) time.Sleep(2*time.Second) fmt.Println(account.GetAccount()) }sync.WaitGroup:用来等待一组子协程的结束,需要设置等待的个数,每个子协程结束后要调用Done(),最后在主协程中Wait()即可
引入
packagemain import( "fmt" ) funcmain(){ //创建通道 ch:=make(chanint) //count表示活动的协程个数 count:=2 gofunc(){ fmt.Println("子协程1") //子协程1执行完成,给通道发送信号 ch<-1 }() gofunc(){ fmt.Println("子协程2") ch<-1 }() //time.Sleep(time.Second) //从ch中不断读数据 forrangech{ count-- ifcount==0{ close(ch) } } }go提供了这种解决方案sync.WaitGroup
Add():添加计数
Done():操作结束时调用,计数减去1
Wait():主函数调用,等待所有操作结束
未完待续...
本文内容总结:Golang-并发编程,1.并行和并发,2.go语言并发优势,3.goroutine是什么,4.创建goroutine,5.runtime包,6.channel是什么,7.channel的基本使用,8.无缓冲的channel,9.有缓冲的channel,10.close(),11.单方向的channel,12.定时器,13.select,14.携程同步锁,
原文链接:https://www.cnblogs.com/konghui/p/10703615.html