使用案例大白话讲解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

posted @ 2021-03-08 16:36  小尾学长  阅读(985)  评论(0编辑  收藏  举报