13_channels_fan-out_fan-in
这一章节比较奇怪,写的都是死循环的输入管道和死循环的输出读取(想到这里,死循环的输出读取居然没报错 ? 应该是如果输出读取是在 goroutine 中,不阻塞主main的话,就不会报错)
做一个测试:
func Read(in chan int) {
for {
fmt.Printf("Read: %d\n", <-in)
}
}
func Send(out chan int) {
for {
n := rand.Intn(10)
out <- n
fmt.Printf("Send: %d\n", n)
}
}
func main() {
in := make(chan int)
go Send(in)
go Send(in)
go Send(in)
go Read(in)
go Read(in)
go Read(in)
time.Sleep(30 * time.Second)
}
反正一直就是正常运行,一直有东西发送。。。但是如果发送方,只发送10个呢,接收方死循环呢,卡主接收方,然后结束
改成这样:
func Read(in chan int) {
for {
fmt.Printf("Read: %d\n", <-in)
}
}
func Send(out chan int) {
defer close(out)
for i := 0; i < 10; i++ {
out <- i
fmt.Printf("Send: %d\n", i)
}
}
func main() {
in := make(chan int)
go Send(in)
go Read(in)
time.Sleep(30 * time.Second)
}
结果发现一个严重问题,一直打印的是Read 0 ,明明 <-in 读取到了不应该是下一个吗,为什么循环Read 0 卡主呢
func Read(in chan int) {
//for {
// fmt.Printf("Read: %d\n", <-in)
//}
fmt.Printf("Read: %d\n", <-in)
fmt.Printf("Read: %d\n", <-in)
fmt.Printf("Read: %d\n", <-in)
}
func Send(out chan int) {
defer close(out)
for i := 0; i < 10; i++ {
out <- i
fmt.Printf("Send: %d\n", i)
}
}
func main() {
in := make(chan int)
go Send(in)
go Read(in)
time.Sleep(30 * time.Second)
}
改成这个样子就是
Read: 0
Send: 0
Send: 1
Read: 1
Read: 2
Send: 2
的卡主,因为只接受了2个,发送方没法继续发送了
这段代码存在严重问题
package main
import (
"fmt"
"time"
)
func Read(in chan int) {
start_time := time.Now()
defer func() {
fmt.Printf("Read run time: %v\n", time.Now().Sub(start_time).Seconds())
}()
for {
fmt.Printf("Read: %d\n", <-in)
}
}
func Send(out chan int) {
start_time := time.Now()
defer func() {
close(out)
fmt.Printf("Send run time: %v\n", time.Now().Sub(start_time).Seconds())
}()
for i := 0; i < 10; i++ {
out <- i
fmt.Printf("Send: %d\n", i)
}
}
func main() {
start_time := time.Now()
defer func() {
fmt.Printf("Main run time: %v\n", time.Now().Sub(start_time).Seconds())
}()
in := make(chan int)
go Send(in)
go Read(in)
time.Sleep(3 * time.Second)
}
- 应该只运行3秒钟,但是在 goland 里面会持续运行十几秒,一直刷新,在外秒命令行运行会变成4s
- 管道读取出来的值全部为0,全是0,且读取不完
对于第2个表现综合表述如下:
改成
for {
fmt.Printf("Read: %d\n", <-in)
time.Sleep(time.Second)
}
休眠1s的话,那么就是正常一发一收的打印,然后到了3秒结束很正常
如果缩短时间
for {
fmt.Printf("Read: %d\n", <-in)
time.Sleep(100 * time.Millisecond)
}
打印结果是
Read: 0
Send: 0
Read: 1
Send: 1
Read: 2
Send: 2
Read: 3
Send: 3
Read: 4
Send: 4
Send: 5
Read: 5
Read: 6
Send: 6
Read: 7
Send: 7
Read: 8
Send: 8
Read: 9
Send: 9
Send run time: 0.905213001
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Read: 0
Main run time: 3.001045732
前期还很正常,但是后期打印出来的全部是0了,为什么后期会打印0呢 ?
对于一个关闭的管道:
- 发送方:任何尝试向已经关闭的管道发送数据的操作都将会导致 panic
- 接收方:可以从已经关闭的管道接收数据,直到管道中的数据被完全读取。一旦管道中所有的数据都被读取完毕,后续的接收操作 【将不会被阻塞】,而是立即返回一个零值,并且 ok 值为 false,表示管道已经关闭,不再有数据可以读取
完全忘记管道是会返回两个值的, val 和 ok ,之所以返回零值,是因为返回的你这个数据类型的0值,是int 就返回int
所以应该读取的时候判断一下 val, ok := <-out
int 管道关闭后,返回的都是0,然后是一个 ok=false, 那么如果是string 管道呢,也是返回0吗,都是用0代表错误吗,试试看
因为要是一个string,然后又要看到变化,所以打算把 变量 i 加入string里面去
package main
import (
"fmt"
"time"
)
func Read(in chan string) {
start_time := time.Now()
defer func() {
fmt.Printf("Read run time: %v\n", time.Now().Sub(start_time).Seconds())
}()
for {
val, ok := <-in
fmt.Printf("Read val: [%v], ok: [%v]\n", val, ok)
time.Sleep(100 * time.Millisecond)
}
}
func Send(out chan string) {
start_time := time.Now()
defer func() {
close(out)
fmt.Printf("Send run time: %v\n", time.Now().Sub(start_time).Seconds())
}()
for i := 0; i < 10; i++ {
msg := "hello, " + string(i)
out <- msg
fmt.Printf("Send: %v\n", msg)
}
}
func main() {
start_time := time.Now()
defer func() {
fmt.Printf("Main run time: %v\n", time.Now().Sub(start_time).Seconds())
}()
in := make(chan string)
go Send(in)
go Read(in)
time.Sleep(3 * time.Second)
}
这个输出是
Read val: [hello, ], ok: [true]
Send: hello,
Read val: [hello, ], ok: [true]
Send: hello,
Read val: [hello,], ok: [true]
Send: hello,
Read val: [hello, ], ok: [true]
Send: hello,
Send run time: 0.906575902
Read val: [], ok: [false]
Read val: [], ok: [false]
Read val: [], ok: [false]
Read val: [], ok: [false]
Main run time: 3.00046913
加入进去的i变量,压根就没有生成到 一开始是怀疑循环遍历 i 的问题,使用
for i := 0; i < 10; i++ {
v := i
//msg := "hello, " + string(i)
msg := "hello, " + string(v)
out <- msg
fmt.Printf("Send: %v\n", msg)
}
也是打印出不来,做一个实验
fmt.Println("hello, " + string(v)
也打印不出来,说明加的有问题,于是考虑用 fmt.Sprintf
msg := fmt.Sprintf("hello, %d", i)
这样子是可以出来的,有效果的
但是如果
fmt.Println("hello, " + string(65)
就会出来字符了,出来的是 A,hello A 这就是说明 string转化的是字符集中的序号,因为我是特意用
>>> ord('A')
65
求出来A的码值然后再去添加打印的
那么如果要 int 转 string 怎么办呢,还要导入新的包,strconv
msg := "hello, " + strconv.Itoa(i)
这样子才可以
sync的atomic 用法
atomic 是 sync/atomic 中一个原子操作函数
var a int64
var wg sync.WaitGroup
func Add() {
a++
wg.Done()
}
func main() {
n := 1000
wg.Add(n)
for i := 0; i < n; i++ {
go Add()
}
wg.Wait()
fmt.Printf("finally n = %d", a)
}
这个如果运行的是10的话,结果是正常的,如果是1000的话,结果是900多少加了,所以要用 atomic
func Add() {
atomic.AddInt64(&a, 1)
wg.Done()
}
注意一定是取地址的加,如果不定义全局变量的话,要传指针了
var wg sync.WaitGroup
func Add(ptr *int64) {
atomic.AddInt64(ptr, 1)
wg.Done()
}
func main() {
n := 1000
var a int64
a = 0
wg.Add(n)
for i := 0; i < n; i++ {
go Add(&a)
}
wg.Wait()
fmt.Printf("finally n = %d", a)
}
注意都是取地址
atomic.AddInt64(&workerID, 1)
thisID := atomic.LoadInt64(&workerID)
挑战
来看这样一段代码,运行时候会报错,要把他调整好
package main
import (
"fmt"
"sync"
)
func main() {
in := gen()
// FAN OUT
// Multiple functions reading from the same channel until that channel is closed
// Distribute work across multiple functions (ten goroutines) that all read from in.
xc := fanOut(in, 10)
// FAN IN
// multiplex multiple channels onto a single channel
// merge the channels from c0 through c9 onto a single channel
for n := range merge(xc...) {
fmt.Println(n)
}
}
func gen() <-chan int {
out := make(chan int)
go func() {
for i := 0; i < 10; i++ {
for j := 3; j < 13; j++ {
out <- j
}
}
close(out)
}()
return out
}
func fanOut(in <-chan int, n int) []<-chan int {
xc := make([]<-chan int, n)
for i := 0; i < n; i++ {
xc = append(xc, factorial(in))
}
return xc
}
func factorial(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- fact(n)
}
close(out)
}()
return out
}
func fact(n int) int {
total := 1
for i := n; i > 0; i-- {
total *= i
}
return total
}
func merge(cs ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
output := func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// Start a goroutine to close out once all the output goroutines are
// done. This must start after the wg.Add call.
go func() {
wg.Wait()
close(out)
}()
return out
}
/*
CHALLENGE #1:
-- This code throws an error: fatal error: all goroutines are asleep - deadlock!
-- fix this code!
*/
分析方法是这样的,我的排查方法是以管道的维度去看, 每一个管道都看什么时候启用,什么时候关闭,什么时候写入,只关注管道,不关注函数功能。结果发现。看起来好像并没有问题,
每个独立的管道是真的没问题
单个没问题 我怀疑应该是整体的问题了 怀疑是 管道数组里面 还在处理 但是管道数组关闭了,但是也有同步 不应该啊
后来经过提醒,是 看一下切片里面放管道有什么要求,引发切片扩容 ?扩容之后是nil
先检查下这段代码,看看wg.Add的时候最终添加了多少 打印出来
fmt.Printf("\n\n\n cs count: %d\n\n\n", len(cs))
wg.Add(len(cs))
打印出来 cs count: 20
长度是20
所以一种解法是这样的,直接改成数组
package main
import (
"fmt"
"sync"
)
func main() {
in := gen()
// FAN OUT
// Multiple functions reading from the same channel until that channel is closed
// Distribute work across multiple functions (ten goroutines) that all read from in.
xc := fanOut(in, 10)
// FAN IN
// multiplex multiple channels onto a single channel
// merge the channels from c0 through c9 onto a single channel
for n := range merge(xc) {
fmt.Println(n)
}
}
func gen() <-chan int {
out := make(chan int)
go func() {
for i := 0; i < 10; i++ {
for j := 3; j < 13; j++ {
out <- j
}
}
close(out)
}()
return out
}
func fanOut(in <-chan int, n int) [10]<-chan int {
//xc := make([]<-chan int, n)
var xc [10]<-chan int
for i := 0; i < n; i++ {
//xc = append(xc, factorial(in))
xc[i] = factorial(in)
}
return xc
}
func factorial(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- fact(n)
}
close(out)
}()
return out
}
func fact(n int) int {
total := 1
for i := n; i > 0; i-- {
total *= i
}
return total
}
func merge(cs [10]<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
output := func(c <-chan int) {
for n := range c {
out <- n
}
wg.Done()
}
fmt.Printf("\n\n\n cs count: %d\n\n\n", len(cs))
wg.Add(len(cs))
for _, c := range cs {
go output(c)
}
// Start a goroutine to close out once all the output goroutines are
// done. This must start after the wg.Add call.
go func() {
wg.Wait()
close(out)
}()
return out
}
/*
CHALLENGE #1:
-- This code throws an error: fatal error: all goroutines are asleep - deadlock!
-- fix this code!
*/
注意数组定义就可以了,不需要初始化,是字典才需要make 显示初始化
但是这里就很烦恼,因为字典我居然在函数定义的时候传形参也要写上个数,同时 ...说是可以拆包,但是我传数组居然没有拆包,所以还有更好的解法
回答疑惑的几个问题,
第一:为什么数组传参的时候,形参也要指定长度:
是的,数组传参的时候也是规定要指定长度的,这就导致很不方便
第二:为什么把写 var out [all_count]chan int 也不行
是的,这个按照规定也不行,数组的长度不允许是变量,一定是固定不变的,除非设置 all_count 为 coonst all_count int 类型才行
第三: 不是说可以拆包吗,为什么拆包 不起作用 ?
拆包是对于切片类型来说的,只有切片才能进行拆包,如果还是切片类型的话,那么拆包是没问题的
在go中,切片的应用还是灵活点,用的比较多,其实像刚才这个情况,直接在初始化的时候写成
var xc []<-chan int
改成切片,不用make的形式,反而就可以了,因为这样切片只会新增一个不会超长
使用切片的append还有一个大坑
a := make([]int, 3)
a = append(a, 1)
a = append(a, 2)
//a = append(a, 3)
fmt.Printf("%T, %v, LEN: %d", a, a, len(a))
这样定义一个切片的时候 ,append两次,切片的长度变成了5, 因为原有的切片没有变。。。只是在后面增加,输出是
[]int, [0 0 0 1 2], LEN: 5
真的是令人困扰的问题啊~~
27 code in process
playing with type
var answer int
answer = 32 / 3.74
这样子会报错,但是其他的类型转化可以,大概就是 等于号左边的无法进行类型转化,最多进行推断
fmt.Println(len("世界"))
这个长度输出的是6,如果你要判断中文的长度,需要转成 rune 切片,或者使用utf8 来计算,比如
runeStr := []rune(str) fmt.Println("字符串中字符的数量:", len(runeStr))
或者
count, _ := utf8.RuneCountInString(str) fmt.Println("字符串中字符的数量:", count)
切片类型不能像Python那要使用负数
全局变量的写法可以是 ,定义的时候就赋值,直接等于号
var a string = "a"
var b int = 2
剩下的貌似不用看了,都是比较细节的了,可以开始全心看框架了第九天就是GolangTraining终结篇了