20200327 go进阶之接口协程信道
目录
昨日内容
// 指针,结构体,方法
指针
//1 指针: 存储变量内存地址的变量(指针有类型--》指向什么类型的指针)
//2 在类型前面加 * 表示指向这个类型的指针
//3 在变量前面加 & 表示取这个变量的地址
//4 在变量前面加 * 表示解引用(反解)
//5 指针的函数传递
//6 不要向函数传递数组指针,而要传递切片
结构体
//1 一系列属性的结合
type 结构体名字 struct {
属性 类型
属性 类型
}
//2 使用结构体 var p 结构体名字 =结构体名字{} 位置,关键字(少些,不按位置)
//3 结构体是值类型,结构体的空值不是nil,是属性的零值
//4 使用 结构体变量.属性 (属性大小写,表示公有私有----》外部包)
//5 匿名结构体 没有名字和type 关键字,定义再函数内部,只使用一次
a:=struct{
属性 类型
属性 类型
}{位置/关键字}
a.属性
//6 匿名字段 字段没有名字 类型就是字段名(同一种类型的匿名字段只能有一个),字段提升
//7 结构体嵌套(结构体套结构体,另一个结构体是匿名,这个结构体中的字段可以提升)
//8 结构体指针
var p *Person=&Person{}
var p *Person
//9 结构体想等性(了解)
方法
//1 方法是绑定给结构体(结构体+方法=面向对象中的类)
//2 格式
func (接收器)方法名()(){
}
//3 绑定给结构体(绑定给Person结构体了)
func (p Person)方法名()(){
}
p.方法名()
//4 值接收器和指针接收器(值接收器:在方法内部不会修改原来的 指针接收器:在方法内部修改原来的)
//5 不管是值接收器还是指针接收器,都可以用值或者指针来调用
//python中的变量---》本质都是引用:地址,
// is 和 == 有什么区别:is 比的是地址 == 比的是值
go进阶
1. 接口
//接口:一系列方法的集合,规范的对象的行为
//
//匿名空接口
//类型断言
//类型选择
//实现多个接口
//接口嵌套
// 接口零值
//1 定义一个鸭子接口(run方法 speak方法)
type DuckInterface interface {
run()
speak()
}
//写一个唐老鸭结构体,实现该接口
type TDuck struct {
name string
age int
wife string
}
//实现接口(只要结构体绑定了接口中的所有方法,就叫做:结构体实现了该接口)
// 同一类事物的多种形态
func (t TDuck) run() {
fmt.Println("我是唐老鸭,我的名字叫", t.name, "我会人走路")
}
func (t TDuck) speak() {
fmt.Println("我是唐老鸭,我的名字叫", t.name, "我说人话")
}
//写一个肉鸭结构体,实现该接口
type RDuck struct {
name string
age int
}
//实现接口(只要结构体绑定了接口中的所有方法,就叫做:结构体实现了该接口)
func (t RDuck) run() {
fmt.Println("我是肉鸭,我的名字叫", t.name, "我会人走路")
}
func (t RDuck) speak() {
fmt.Println("我是肉鸭,我的名字叫", t.name, "我说人话")
}
1. 空接口
// 1 空接口(所有类型都实现了空接口)
type Empty interface {
}
2.匿名空接口
var a interface{}="xxx"
fmt.Println(a)
3. 类型断言
i1 := TDuck{"嘤嘤怪",17,"刘亦菲"}
i2 := RDuck{"怪五",18}
//两个类型都实现了Duckinterface接口,可以当做Duckinterface类型
test(i1)
func test(i Duckinterface){
//接口类型没有属性,,娶不到鸭子的name
//断言: 断定i是TDuck类型
var t TDuck=i.(TDuck) //如果断言失败就报错
fmt.Println(t.wife)
}
4. 类型选择
i1 := TDuck{"嘤嘤怪",17,"刘亦菲"}
i2 := RDuck{"怪五",18}
i3 :=1
i4 :="xx"
test(i1)
test(i2)
test(i3)
test(i4)
test(4.4)
func test(i interface{}){ //可以接受任意类型的参数
//进行类型的选择
//switch i.(type) {
switch a := i.(type) {
case int:
fmt.Println("我是int")
case string:
fmt.Println("我是string")
case TDuck:
fmt.Println("我是TDuck")
fmt.Println(a.wife)
case RDuck:
fmt.Println("我是RDuck")
fmt.Println(a.name)
default:
fmt.Println("未知类型")
}
}
5. 实现多个接口
type SalaryCalculator interface {
DisplaySalary()
}
type LeaveCalculator interface {
CalculateLeavesLeft() int
}
type Employee struct {
firstName string
lastName string
}
func (e Employee)DisplaySalary() {
fmt.Println("xxxxx")
}
func (e Employee)CalculateLeavesLeft() int {
return 1
}
4 实现多个接口,就可以赋值给不通的接口类型
var a SalaryCalculator
var b LeaveCalculator
var c Employee=Employee{}
a=c
b=c
6. 接口嵌套
// 接口嵌套
type SalaryCalculator interface {
DisplaySalary()
run()
}
type LeaveCalculator interface {
SalaryCalculator
//DisplaySalary()
//run()
CalculateLeavesLeft()
}
type Employee struct {
firstName string
lastName string
}
// 接口嵌套
var c Employee=Employee{}
var b LeaveCalculator
b=c
7. 接口零值
是nil类型,接口类型是引用类型
var a LeaveCalculator
fmt.Println(a)
2. go协程 goroutine
goroutine:go的协程(并不是真的协程:统称,有线程也有协程)线程池 go 任务丢到线程池中
并发和并行
- 并发:假如在他晨跑时,鞋带突然松了。于是他停下来,系一下鞋带,接下来继续跑
- 并行: 假如这个人在慢跑时,还在用他的 iPod 听着音乐(必须多核cpu)
- 线程:是cpu调度的最小单位 协程:单线程下实现并发,微线程,用户自己控制的线程
使用 go
package main
import (
"fmt"
"time"
)
func test1() {
fmt.Println("go go go")
}
func main() {
// go 关键字,就并发起来了
fmt.Println("我是mian")
//go test1()
//go test1()
//go test1()
//go test1()
//go test1()
for i:=0;i<10000000;i++{
go test1()
}
time.Sleep(1*time.Second)
}
3. 信道 channle
信道:信道可以想像成 Go 协程之间通信的管道。如同管道中的水会从一端流到另一端,通过使用信道,数据也可以从一端发送,在另一端接收。
- 信道就是一个变量
- channle:通道/信道
1. 定义信道 chan
var a chan int
2. 信道零值(nil:引用类型)
fmt.Println(a) // nil
3. 通过信道进行发送和接收
// 在信道中放值
var b chan int = make(chan int)
b <- 1 //向管道中放 1
// <- b // 从管道中取值
var c = <- b // 从管道中取值并赋值
func mian (){
// 定义初始化一个int类型的信道(make来初始化引用类型)
var a chan int= make(chan int)
// 引用类型,不需要传地址
go test(a)
c := <- a
fmt.Println(c)
}
func test(a chan int){
fmt.Println("ggogogo")
// 执行完成,在信道中放值
a <- 1
}
4. 发送和接收,默认都是阻塞的
当go协程给一个信道发送数据时,需要有其他go协程来接受数据,没有的话,程序触发panic,形成死锁
var b chan int =make(chan int)
b <- 1 // 报错 死锁
5. 单向信道
- 单向信道: 只写或只读
- 双向信道: 可以写,可以读
package main
import "fmt"
func sendData(sendch chan<- int) { //转成只写
sendch <- 10
}
func main() {
sendch := make(chan<- int) //只能写的信道,只能向里面放值
go sendData(sendch)
fmt.Println(<-sendch) //取值就报错
}
6. 信道循环
package main
import (
"fmt"
)
func producer(chnl chan int) {
for i := 0; i < 10; i++ {
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for v := range ch {
fmt.Println("Received ",v)
}
}
7. 案例
package main
import (
"fmt"
)
func calcSquares(number int, squareop chan int) {
sum := 0
for number != 0 {
digit := number % 10
sum += digit * digit
number /= 10
}
squareop <- sum
}
func calcCubes(number int, cubeop chan int) {
sum := 0
for number != 0 {
digit := number % 10
sum += digit * digit * digit
number /= 10
}
cubeop <- sum
}
func main() {
number := 589
sqrch := make(chan int)
cubech := make(chan int)
go calcSquares(number, sqrch)
go calcCubes(number, cubech)
squares, cubes := <-sqrch, <-cubech
fmt.Println("Final output", squares + cubes)
}
4. 缓冲信道
有缓冲信道:管子可以放多个值
1. 定义
func main() {
//1 定义
var a chan int =make(chan int,0) //无缓冲信道
var a chan int =make(chan int,5) //定义一个长度为5的有缓冲信道
//可以塞5个 int,在塞满之前,是不阻塞的
a<-1
a<-2
a<-3
a<-4
a<-5
a<-6 //超了5个,会出现死锁
fmt.Println(<-a)
fmt.Println(<-a)
fmt.Println(<-a)
fmt.Println(<-a)
fmt.Println(<-a)
fmt.Println(<-a)// 取第6个,死锁
//2 长度和容量
//len cap
var a chan int =make(chan int,5)
a<-2
a<-3
fmt.Println(len(a)) //2
fmt.Println(cap(a)) //5
}
长度和容量
//len cap
var a chan int =make(chan int,5)
a<-2
a<-3
fmt.Println(len(a)) //2
fmt.Println(cap(a)) //5
5. defer与panic 与recover(异常处理)
1. defer
延迟执行 : 先进行注册,等函数执行完成,再倒着执行defer的内容
func main() {
defer fmt.Println("我最后执行") //先注册,等函数执行完成,再倒着执行defer的内容
defer fmt.Println("888888")
fmt.Println("xxx")
panic("严重出错了")
fmt.Println("yyyy")
}
---------------打印---------------
xxx
888888
我最后执行
panic:严重出错
2. panic
主动抛出异常 python中raise
panic ("我出现异常了")
3. recover
恢复程序,继续执行
func f1() {
fmt.Println("f1")
}
func f2() {
//defer fmt.Println("我会执行")
//在这捕获异常,把异常解决掉
defer func() {
if err := recover(); err != nil { //recover执行,如果等于nil 表示没有异常,如果有值,表示出错,err就是错误信息
fmt.Println(err)
}
fmt.Println("我是finally的东西")
}()
fmt.Println("f2")
panic("我出异常了") //这里系统报错,整个程序退出
fmt.Println("我不会执行") // 永远不会执行
defer fmt.Println("xxx") //如果没有注册,肯定不会执行
}
func f3() {
fmt.Println("f3")
}
func main() {
f1()
f2()
f3()
}
4. 总结
终极总结异常处理,以后要捕获哪的异常,就在哪写
defer func() {
if err := recover(); err != nil { //recover执行,如果等于nil 表示没有异常,如果有值,表示出错,err就是错误信息
fmt.Println(err)
}
fmt.Println("我是finally的东西")
}()
func main() {
fmt.Println("xxx")
defer func() {
if err := recover(); err != nil { //recover执行,如果等于nil 表示没有异常,如果有值,表示出错,err就是错误信息
fmt.Println(err)
}
fmt.Println("我是finally的东西")
}()
panic("我出异常了")
}
func main() {
fmt.Println("xxx")
fmt.Println("uyyy")
defer fmt.Println("7777")
fmt.Println("yyyeee")
//打开文件
//立马写一个defer 文件对象.close()
//处理文件
//趴。出异常了,程序崩掉
//从这个位置开始,倒着执行defer
}