zoukankan      html  css  js  c++  java
  • 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
    }
    
    
  • 相关阅读:
    vue中使用v-bind="$attrs"和v-on="$listeners"进行多层组件监听
    钉钉小程序开发遇到的坑
    promise、async、await、settimeout异步原理与执行顺序
    js获取url参数值的几种方式
    ES6解构赋值
    2019年前端面试题
    浏览器兼容问题踩坑收集
    react+antd分页 实现分页及页面刷新时回到刷新前的page
    无侵入埋点
    CSS学习笔记(三)空白符和换行
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12734569.html
Copyright © 2011-2022 走看看