风离不摆烂学习日志 Day3 --- Go 协程与Channel管道 练习题
AI-摘要
Tianli GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
风离不摆烂学习日志 Day3 — Go 协程与Channel管道 练习题
题目描述
使用goroutine和channe1实现一个计算int64随机数各位数和的程序 1.开启一个goroutine循环生成int64类型的随机数,发送到jobchan 2.开启24个goroutine从jobhan中取出随机数计算各位数的和,将结果发送到resultChan 3.主goroutine从resultchan取出结果并打印到终端输出
代码详解
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
/**
使用goroutine和channe1实现一个计算int64随机数各位数和的程序
1.开启一个goroutine循环生成int64类型的随机数,发送到jobchan
2.开启24个goroutine从jobhan中取出随机数计算各位数的和,将结果发送到resultChan
3.主goroutine从resultchan取出结果并打印到终端输出
*/
type job struct { //定义结构体 job
num int64
}
type result struct { //定义结构体 result 包含 job 中 num 和 num 各个数相加的 sum
job *job
sum int64
}
/**
初始化 jobChan 给定100初始空间 chan 类型为 job的指针 除了 int 类型以外 其他类型存储为其引用地址
*/
var jobChan = make(chan *job, 100)
var resultChan = make(chan *result, 100)
var wg = sync.WaitGroup{}
/**
生产者 传入 jobChan 向 其中传入 64位随机数据
*/
func producer() {
defer wg.Done()
// 循环生成int64的随机数,发送到jobChan
for {
x := rand.Int63()
newJob := &job{
num: x,
}
jobChan <- newJob
time.Sleep(time.Second)
}
}
func consumer() {
defer wg.Done()
// 从jobChan中取出随机数计算各位数的和,将结果发送给resultChan
for {
job := <-jobChan
sum := getSum(job.num)
newResult := &result{
job: job,
sum: sum,
}
resultChan <- newResult
}
}
func getSum(num int64) (sum int64) {
/**
123 3
12 3+2
1 3+2+1
*/
for {
sum = sum + num%10
num = num / 10
if num < 1 {
break
}
}
return sum
}
func main() {
wg.Add(1)
go producer()
//开启24个goroutine执行consumer
wg.Add(24)
for i := 0; i < 24; i++ {
go consumer()
}
// 主goroutine从resultChan中取出结果并打印
for result := range resultChan {
fmt.Printf("value:%d sum:%d\n ", result.job.num, result.sum)
}
wg.Wait()
}
Go Channel 的遍历
-
阻塞接收数据 阻塞模式接收数据时,将接收变量作为<-操作符的左值
data := <-ch // 执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。
-
非阻塞接收数据 使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下:
func readChan(c chan int) (int, error) {
select {
case num := <-c:
return num, nil
default:
return 0, errors.New("chan do not have data")
}
}
// 加上超时时间
func readChanWithTimeout(c chan int) (int, error) {
timeout := time.NewTimer(time.Microsecond * 100)
select {
case num := <-c:
return num , nil
case <-timeout.C:
return 0, errors.New("read chan time out")
}
}
for range 遍历
结论
在遍历时,如果channel没有关闭,则会出现deadlock的错误
在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
package main
import (
"fmt"
)
func main() {
//遍历管道
intChan2 := make(chan int, 100)
for i := 0; i < 100; i++ {
intChan2 <- i * 2 //放入100个数据到管道
}
//遍历管道不能使用普通的 for 循环
// for i := 0; i < len(intChan2); i++ {
// }
//在遍历时,如果channel没有关闭,则会出现deadlock的错误
//在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
//close(intChan2)
for v := range intChan2 { //没有下标
fmt.Println("v=", v)
}
}
Go select
Go里面提供了一个关键字select,通过select可以监听channel上的数据流动。
select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。
与switch语句可以选择任何可使用相等比较的条件相比, select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作,大致的结构如下:
select {
case <-chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}
在一个select语句中,Go语言会按顺序从头至尾评估每一个发送和接收的语句。
如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:
- 如果给出了default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复。
- 如果没有default语句,那么select语句将被阻塞,直到至少有一个通信可以进行下去。
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员风离
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果