使用案例大白话讲解Go语言并发编程go chan select close
使用案例大白话讲解Go语言并发go chan select close
1、初识Go并发-go关键字实现多线程
使用go 关键字可以开辟一个新的协程(线程)线程之间并行执行
package main
import (
"fmt"
"time"
)
func printData(data string) { //循环打印数据
for i:=0;i<5;i++{
time.Sleep(time.Second)
fmt.Println(data,":",i)//打印数据
}
}
func main() {
printData("主程序") //函数调用
//主程序打印完了才会到下面的协程执行
go printData("子程序1") //多线程的并发
go printData("子程序2")
go func(msg string) {fmt.Println(msg)}("子程序3 Go!Go!Go!")//匿名函数风格
//主程序一旦执行结束,子程序都会结束,所以这里睡10秒
time.Sleep(time.Second*10)
}
运行结果如下:
主程序 : 0
主程序 : 1
主程序 : 2
主程序 : 3
主程序 : 4
子程序3 Go!
子程序1 : 0
子程序2 : 0
子程序2 : 1
子程序1 : 1
子程序1 : 2
子程序2 : 2
子程序2 : 3
子程序1 : 3
子程序1 : 4
子程序2 : 4
2、协程与协程之间的通信chan的使用
package main
import "fmt"
func main() {
messages:=make(chan string) //创建了一个通道。
go func() {messages<-"Hello Chan!!"}()//发送数据给通道
fmt.Println(messages) //消息通道内存地址
msg:=<-messages //从通道里面拿出字符串赋值给msg
fmt.Println(msg)
messages2:=make(chan string,2)//通道接收多个消息,通道返回结果,2表示通道一次可以容纳2条消息
messages2<-"hello baby" //往通道里面放值
messages2<-"hello Golang"
fmt.Println(<-messages2)
fmt.Println(<-messages2)
}
执行结果如下:
0xc000100060
Hello Chan!!
hello baby
hello Golang
3、使用协程与通道实现同步
package main
import "fmt"
import "time"
func worker(done chan bool){
fmt.Println("开始干活")
time.Sleep(time.Second*3) //干活
fmt.Println("活干完了")
done<-true //返回数据,我们干完活了
}
func main() {
done := make(chan bool, 1) //创建一个通道,传递数据
go worker(done) //用done接收消息
<-done //锁住,一直等待下去,直到接受到结果
fmt.Println("game over")
}
执行结果如下:
开始干活
活干完了
game over
4、实现简单的通信线路
package main
import "fmt"
//一个参数发送,一个参数用于接收
func send(sendStrings chan string, msg string) {
sendStrings<-msg //将msg放到发送的通道中
}
//一个参数发送,一个参数用于接收
func receive(sendStrings chan string,receiveString chan string) {
msg := <-sendStrings
receiveString <-msg
}
func main() {
sendChan := make(chan string ,1)
receiveChan := make(chan string ,1)//两个通道
send(sendChan,"发送一个消息:你好Chan")//调用
receive(sendChan,receiveChan)
fmt.Println(<-receiveChan)//显示通信
}
执行结果如下:
发送一个消息:你好Chan
5、select的使用
简单的介绍select的使用
package main
import (
"fmt"
)
func main(){
var c1,c2,c3 chan int
var i1,i2 int
c1 = make(chan int,1)
c1 <- 10
c3 = make(chan int,1)
c3 <- 30
//select 类似于switch语句,case满足条件即可执行
//注意 select 的 case 如果同一时间有多个条件满足,会从满足的条件中随机一个执行,
//例如以下例子偶尔执行结果是 received 30 from c3 偶尔结果是 received 10 from c1
select{
case i1 = <- c1:
fmt.Println("received",i1,"from c1")
case c2 <- i2:
fmt.Println("send",i2,"to c2")
case i3,ok := (<- c3):
if ok{
fmt.Println("received",i3,"from c3")
}else {
fmt.Println("c3 is closed")
}
default:
fmt.Println("no communication")
}
//time.Sleep(time.Second*3)
}
6、关于select的简单的例子
package main
import "fmt"
import "time"
func main() {
c1:=make(chan string) //选择两个通道
c2:=make(chan string)
go func() {
time.Sleep(time.Second*3) //3秒
c1<-"第一条信息"
}()
go func() {
time.Sleep(time.Second*2) //2秒
c2<-"第二条信息"
}()
//如果将此处的for循环中的2改为3会报一个错误"fatal error: all goroutines are asleep - deadlock!"
//因此go语言会监测通道的生命周期 当协程执行结束不会有新信息往通道里面扔数据了那么goroutines 就会 asleep(睡眠)
for i:=0;i<2;i++{
select{
case msg1:= <-c1:
fmt.Println("received",msg1)
case msg2:=<-c2:
fmt.Println("received",msg2)
}
fmt.Println("over!")
}
}
执行结果如下:
received 第二条信息
over!
received 第一条信息
over!
7、select模拟实现访问超时案例
package main
import "fmt"
import "time"
func main(){
c1:=make(chan string,1)//传递消息的通道
go func() {
time.Sleep(time.Second*2) //更改这里的值可以查看不同的效果
c1<-"结果 num one"
}()
select {
case res:= <-c1:
fmt.Println(res)
case <-time.After(time.Second*3):
fmt.Println("timeout 1")
}
}
8、select实现阻塞与非阻塞接收信息
package main
import (
"fmt"
"time"
)
func main() {
messages :=make(chan string)
signals:=make(chan bool)
//非阻塞接收,有可以用就直接用,没有就默认,不等待
fmt.Println("程序开始执行...")
go func() {
time.Sleep(time.Second*2)
messages<-"结果 num one"
}()
//如果存在default 那么就是非阻塞 有可以用就直接用,没有就默认,不等待。
//如果将下面的default语句块注释,那么select会等待接收messages中的值。
select {
case msg:= <-messages:
fmt.Println("A 收到消息",msg)
default:
fmt.Println("C 没有消息收到")
}
msg:="hi baby"
select {
case messages<-msg:
fmt.Println("B sent message",msg)
default:
fmt.Println("B no message sent")
}
select {
case msg:= <-messages:
fmt.Println("C收到消息",msg)
case sig := <-signals:
fmt.Println("C收到消息",sig)
default:
fmt.Println("C no activity")
}
}
9、通道关闭 close
package main
import (
"fmt"
"time"
)
func main() {
jobs :=make(chan int,5) //通道,返回结果数据
done:=make(chan bool)//通道
go func() {
for {
j,ok:= <-jobs
//fmt.Printf("ok的类型为%T\n",ok) //ok的类型为bool 当通道关闭ok的类型为false
if ok{
fmt.Println("收到工作",j)
} else {
fmt.Println("收到全部工作结果")
done <- true //其实这里放true和false都无所谓
}
}
}()
for j:=1;j<=3;j++{
time.Sleep(time.Second)
jobs<-j
fmt.Println("sent job",j)
}
close(jobs)
fmt.Println("发送完毕")
//等待工作
<-done
}
执行结果如下:
sent job 1
收到工作 1
收到工作 2
sent job 2
sent job 3
发送完毕
收到工作 3
收到全部工作结果
收到全部工作结果
10、简单实战之工作池
package main
import "fmt"
import "time"
//程序目的:雇了三个工人给我打工,我发布工作,三个工人抢着干活。干完了把结果返回给我。jobs相当于是工作池
//工作协程,两个通道,
//id编号,jobs工作编号,result 结果
//其实worker(id int,jobs<-chan int,result chan <-int) 中的<-都可以去掉 你也可以写成worker(id int,jobs chan int,result chan int)
func worker(id int, jobs chan int, result chan int) {
for j:=range jobs{
fmt.Println("worker",id,"开始干活",j)
time.Sleep(time.Second)
fmt.Println("worker",id,"结束干活",j)
result <- j*10
}
}
func main() {
jobs := make(chan int,100) //工作
result := make(chan int,100)//结果
//开启三个工作协程序,
for w:=1; w<=3;w++{
go worker(w,jobs,result)
}
for j:=1;j<=5;j++{
jobs<-j //发布五个工作
}
close(jobs)//关闭jobs通道。
for i:=1;i<=5;i++{
fmt.Println("result:",<-result)
}
}
程序执行结果:
worker 3 开始干活 1
worker 2 开始干活 3
worker 1 开始干活 2
worker 1 结束干活 2
worker 1 开始干活 4
result: 20
worker 3 结束干活 1
worker 2 结束干活 3
worker 2 开始干活 5
result: 10
result: 30
worker 1 结束干活 4
result: 40
worker 2 结束干活 5
result: 50
分类:
Go语言
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· 因为Apifox不支持离线,我果断选择了Apipost!