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 任务丢到线程池中

img

并发和并行

    - 并发:假如在他晨跑时,鞋带突然松了。于是他停下来,系一下鞋带,接下来继续跑
    - 并行: 假如这个人在慢跑时,还在用他的 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 	// 从管道中取值并赋值

img

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. 信道循环

img

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
}

posted @ 2020-04-19 22:12  fwzzz  阅读(144)  评论(0编辑  收藏  举报