package main
import (
"fmt"
"time"
)
func main() {
c := make(chan int, 10)
go fibonacci(cap(c), c)
for v := range c {
fmt.Println("out:", time.Now())
fmt.Println(v)
}
}
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i :=0; i < n; i++ {
c <- x
fmt.Println("in:",time.Now())
time.Sleep(100)
x, y = y, x+y
}
close(c)
}
2180Go 并发
更好的展示边入边出概念:
package main import ( "fmt" "time" ) func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for v := range c { fmt.Println("out:", time.Now()) fmt.Println(v) } } func fibonacci(n int, c chan int) { x, y := 0, 1 for i :=0; i < n; i++ { c <- x fmt.Println("in:",time.Now()) time.Sleep(100) x, y = y, x+y } close(c) }2179Go 并发
我们单独写一个 say2 函数来跑 goroutine,并且 Sleep 时间设置长一点,150 毫秒,看看会发生什么:
package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s, (i+1)*100) } } func say2(s string) { for i := 0; i < 5; i++ { time.Sleep(150 * time.Millisecond) fmt.Println(s, (i+1)*150) } } func main() { go say2("world") say("hello") }输出结果:
问题来了,say2 只执行了 3 次,而不是设想的 5 次,为什么呢?
原来,在 goroutine 还没来得及跑完 5 次的时候,主函数已经退出了。
我们要想办法阻止主函数的结束,要等待 goroutine 执行完成之后,再退出主函数:
package main import ( "fmt" "time" ) func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s, (i+1)*100) } } func say2(s string, ch chan int) { for i := 0; i < 5; i++ { time.Sleep(150 * time.Millisecond) fmt.Println(s, (i+1)*150) } ch <- 0 close(ch) } func main() { ch := make(chan int) go say2("world", ch) say("hello") fmt.Println(<-ch) }我们引入一个信道,默认的,信道的存消息和取消息都是阻塞的,在 goroutine 中执行完成后给信道一个值 0,则主函数会一直等待信道中的值,一旦信道有值,主函数才会结束。
2178Go 并发
goroutine 是 golang 中在语言级别实现的轻量级线程,仅仅利用 go 就能立刻起一个新线程。多线程会引入线程之间的同步问题,在 golang 中可以使用 channel 作为同步的工具。
通过 channel 可以实现两个 goroutine 之间的通信。
创建一个 channel, make(chan TYPE {, NUM}) TYPE 指的是 channel 中传输的数据类型,第二个参数是可选的,指的是 channel 的容量大小。
向 channel 传入数据, CHAN <- DATA , CHAN 指的是目的 channel 即收集数据的一方, DATA 则是要传的数据。
从 channel 读取数据, DATA := <-CHAN ,和向 channel 传入数据相反,在数据输送箭头的右侧的是 channel,形象地展现了数据从隧道流出到变量里。
2177Go 错误处理
个人多次试验,总结几点 panic,defer 和 recover。
package main import ( "fmt" ) func main() { fmt.Println("外层开始") defer func() { fmt.Println("外层准备recover") if err := recover(); err != nil { fmt.Printf("%#v-%#v\n", "外层", err) // err已经在上一级的函数中捕获了,这里没有异常,只是例行先执行defer,然后执行后面的代码 } else { fmt.Println("外层没做啥事") } fmt.Println("外层完成recover") }() fmt.Println("外层即将异常") f() fmt.Println("外层异常后") defer func() { fmt.Println("外层异常后defer") }() } func f() { fmt.Println("内层开始") defer func() { fmt.Println("内层recover前的defer") }() defer func() { fmt.Println("内层准备recover") if err := recover(); err != nil { fmt.Printf("%#v-%#v\n", "内层", err) // 这里err就是panic传入的内容 } fmt.Println("内层完成recover") }() defer func() { fmt.Println("内层异常前recover后的defer") }() panic("异常信息") defer func() { fmt.Println("内层异常后的defer") }() fmt.Println("内层异常后语句") //recover捕获的一级或者完全不捕获这里开始下面代码不会再执行 }代码执行的结果:
2176Go 错误处理
fmt.Println 打印结构体的时候,会把其中的 error 的返回的信息打印出来。
type User struct { username string password string } func (p *User) init(username string ,password string) (*User,string) { if ""==username || ""==password { return p,p.Error() } p.username = username p.password = password return p,""} func (p *User) Error() string { return "Usernam or password shouldn't be empty!"} } func main() { var user User user1, _ :=user.init("",""); fmt.Println(user1) }结果: