go语言5 接口, 并发与并行, go协程, 信道, 缓冲信道, 异常处理, python进程线程

1 接口

//一系列方法的集合,规范了子类的行为,    
    -python和go都属于鸭子类型,非侵入式接口
    -java:侵入式接口
package main

import "fmt"

//1 定义一个接口,(规范行为,没有具体实现)
type DuckInterface interface {
    Run()
    Speak()
}

//定义一个TDuck结构体
type TDuck struct {
    name string
    age int
    wife string
}
//TDuck实现接口(实现接口中的所有方法)
func (t TDuck)Run()  {
    fmt.Println("我是唐老鸭,我的名字是",t.name,"我说人话")
}
func (t TDuck)Speak()  {
    fmt.Println("我是唐老鸭,我的名字是",t.name,"我学人走路")
}

//定义一个RDuck结构体
type RDuck struct {
    name string
    age int
}
//RDuck实现接口(实现接口中的所有方法)
func (t RDuck)Run()  {
    fmt.Println("我是普通肉鸭,我的名字是",t.name,"我阿嘎嘎叫")
}
func (t RDuck)Speak()  {
    fmt.Println("我是普通肉鸭,我的名字是",t.name,"我歪歪扭扭走路")
}


// 5 有名空接口
type EmptyInterface interface {

}

func main() {
    //2 实例化得到TDuck和RDuck两个对象
    //t:=TDuck{"鸡哥",88,"凤姐"}
    r:=RDuck{"普通鸭子",2}
    //t.Run()
    //t.Speak()
    //r.Run()
    //r.Speak()
    //接口也是一个类型(可以定义一个变量是接口类型)
    // 同一类事物的多种形态
    //var d DuckInterface
    //d=t
    ////d=r
    //d.Speak()
    //d.Run()
    //想再去t的属性,name,age,wife
    //test(t)
    //test(r)

    // 5 空接口
    //-只要一个类型,实现接口所有的方法,就叫实现该接口
    //-如果一个接口是空的,一个方法都没有,所有类型都实现了空接口
    //-任意类型,都可以赋值给空接口类型
    //var a EmptyInterface
    //a=1
    //a="xx"
    //a=[3]int{1,2,3}

    //6 接口类型空值(nil)
    //var a EmptyInterface
    //fmt.Println(a)

    //7 匿名空接口
    // interface{}
    test(1)
    test(r)
    test(1.2)
}

////3 类型断言
//func test(d DuckInterface)  {
//    //d.Run()
//    //d.Speak()
//    //var t TDuck
//    //t=d.(TDuck)
//    t:=d.(TDuck) //断言d是TDuck类型,如果正确,就会把d转成t,否则会报错
//    fmt.Println(t.name)
//    fmt.Println(t.wife)
//    t.Speak()
//    t.Run()
//}

//4 类型选择
//func test(d DuckInterface)  {
//    switch a:=d.(type) {
//    case TDuck:
//        fmt.Println(a.wife)
//        fmt.Println("你是TDuck类型")
//    case RDuck:
//        fmt.Println(a.name)
//        fmt.Println("你是RDuck类型")
//    default:
//        fmt.Println("不知道是什么类型")
//    }
//}

//7 匿名空接口
//func test(i interface{})  {  // 也可以
func test(i EmptyInterface)  {  // EmptyInterface是上面定义的有名匿名接口
    fmt.Println(i)
}
package main

// 1 实现多个接口
//type DuckInterface1 interface {
//    Run()
//    Speak()
//}
//
//type HumanInterface interface {
//    Drive()
//}
//
////定义一个TDuck结构体
//type TDuck1 struct {
//    name string
//    age int
//    wife string
//}
////TDuck实现接口(实现接口中的所有方法)
//func (t TDuck1)Run()  {
//    fmt.Println("我是唐老鸭,我的名字是",t.name,"我说人话")
//}
//func (t TDuck1)Speak()  {
//    fmt.Println("我是唐老鸭,我的名字是",t.name,"我学人走路")
//}
////实现HumanInterface接口
//func (t TDuck1)Drive()  {
//    fmt.Println("我是唐老鸭,我开车")
//}

// 2 接口嵌套
type DuckInterface1 interface {
    Run()
    Speak()
}

type HumanInterface interface {       // 要实现这个接口,必须实现Drive(),Run(),Speak()方法
    DuckInterface1
    //相当于
    //Run()
    //Speak()
    Drive()
}

func main() {
    //t:=TDuck1{"鸡哥",18,"凤姐"}
    //var d DuckInterface1
    //var h HumanInterface
    //d=t
    //d.Run()
    //d.Speak()
    //h=t
    //h.Drive()

}

2 并发和并行

//并发:假如在他晨跑时,鞋带突然松了。于是他停下来,系一下鞋带,接下来继续跑
//并行:如这个人在慢跑时,还在用他的 iPod 听着音乐

3 go协程

//go协程--》goroutine,并不是真正的协程(线程+协程,语言层面处理了,不需要开发者去关注) 
// 如果要go线程并行,需要有个参数配置

package main

import (
    "fmt"
    "time"
)

func hello()  {
    fmt.Println("go go go!")
}
func main() {
    fmt.Println("主函数开始")
    go hello() //通过go关键字,开启goroutine,并发执行
    go hello()
    go hello()
    go hello()

    fmt.Println("主函数结束")
    time.Sleep(2*time.Second)
}

4 信道

//goroutine直接通信
//go语言不推崇共享变量方法做通信,而推崇管道通信channel(信道)
// 信道(管道)
package main
import (
    "fmt"
    "time"
)

func main() {
    //1 信道也是一个变量(需要指明运输的类型)
    //var a chan int //定义一个int类型信道

    //2 信道的0值,nil类型,它是一个引用
    //fmt.Println(a)

    //3 信道初始化
    //var a chan int=make(chan int)
    //fmt.Println(a)

    //4 信道的放值,和取值
    //var a chan int=make(chan int)
    //a<-1   //放值,把1放到信道中
    ////var b int=<-a    //取值
    //<-a    //取值

    //5 默认情况下,信道的放值和取值都是阻塞的(一次一个都放不进去) (放值必须有人来接,否则放不进去)
    fmt.Println("开始")
    var a chan bool=make(chan bool)
    //信道是引用类型
    go hello1(a)
    // time.Sleep(2*time.Second)
    <-a        // hang住,只有里面有值,取出
}

func hello1(a chan bool)  {
    fmt.Println("go go go")
    a<-true      // 当如果没有人取值,会一直hang住,知道有人取为去,才能放入
    fmt.Println("xxx")  // 当主线程取完值,控制台运行结束,不会打印xxx
}
//信道案例

package main
/*
输入 453  计算每一位的平方和和每一位的立方和的和
squares = (4 * 4) + (5 * 5) + (3 * 3)
cubes = (4 * 4 * 4) + (5 * 5 * 5) + (3 * 3 * 3)
output = squares + cubes
 */

import (
    "fmt"
)

func calcSquares(number int, squareop chan int) {
    sum := 0
    for number != 0 {
        digit := number % 10   //% 取余数 453对10取余数--》3--》5--》4
        sum += digit * digit
        number /= 10          // /除以  453除以10----》45--》5--》0
    }
    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 := 4535
    sqrch := make(chan int)
    cubech := make(chan int)
    go calcSquares(number, sqrch)
    go calcCubes(number, cubech)
    squares, cubes := <-sqrch, <-cubech
    //squares:= <-sqrch
    //cubes := <-cubech
    fmt.Println("Final output", squares + cubes)
}
package main
func main() {
    //1 死锁

    //var a chan int=make(chan int)
    //a<-1  //一直阻塞在这,报死锁错误
    //<-a     //一直阻塞在这,报死锁错误

    //2 单向信道(只写或者只读)
    //sendch := make(chan int) //定义一个可写可读信道
    //go sendData(sendch)
    //fmt.Println(<-sendch)  //只能往里写值,取值报错

    //3 信道的关闭 close
    //sendch := make(chan int)
    //close(sendch)

}
func sendData(sendch chan<- int) {  //转成只写信道
    sendch <- 10
    //<-sendch  //只要读就报错
}
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)
    }
}

5 缓冲信道

//有缓冲信道(信道可以放多个值)

package main
import (
    "fmt"
    "sync"
    "time"
)

func main() {
    //1 有缓冲信道的定义和死锁问题
    //var a chan int =make(chan int,4)  //长度为4的有缓冲信道
    //a<-1
    //a<-2
    ////a<-3
    ////a<-4
    ////管子满了
    ////a<-5  //报死锁错误
    ////推断出无缓冲信道 var a chan int =make(chan int,0)
    //fmt.Println(<-a)
    //fmt.Println(<-a)
    ////管子没有东西了,再取,报死锁
    //fmt.Println(<-a)

    //2 信道的容量和长度
    //长度是目前管道中有几个值,容量是管道最多能容纳多少值
    //var a chan int =make(chan int,4)
    //fmt.Println(len(a))
    //fmt.Println(cap(a))
    //a<-1
    //a<-2
    //fmt.Println(len(a))
    //fmt.Println(cap(a))

    //3 小案例(通过信道实现goroutine的同步)    第一种实现线程同步方式(常用)
    //var a chan int =make(chan int,3)
    ////b:=<-a
    //go test3(a)
    //
    //fmt.Println(<-a)
    //
    //fmt.Println(<-a)

    //4 通过waitgroup实现同步       第二种实现线程同步方式
        no := 3
        var wg sync.WaitGroup  //值类型,没有初始化,有默认值
        for i := 0; i < no; i++ {
            wg.Add(1)
            go process(i, &wg)   // 因为是值类型,所以要取地址
        }
        wg.Wait() //add了几次,必须有几个wg.Done()对应,否则一直等在这
        fmt.Println("All go routines finished executing")
}

func process(i int, wg *sync.WaitGroup) {
    fmt.Println("started Goroutine ", i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended\n", i)
    wg.Done()
}


//func test3(a chan int)  {
//    a<-1
//
//    time.Sleep(time.Second*2)
//    fmt.Println("假设我在运算")
//
//    a<-2
//    close(a)
//
//}

6 异常处理

//异常处理
package main

import "fmt"

//func main() {
//    f1()
//    f2()
//    f3()
//}
//func f1()  {
//    fmt.Println("f1")
//}
//
//func f2()  {
//    fmt.Println("f2")
//    //如果这个地方出了异常
//}
//func f3()  {
//    fmt.Println("f3")
//}

//defer:延迟执行,即便程序出现严重错误,也会执行
//panic:主动抛出异常 raise
//recover:恢复程序,继续执行

//func main() {
//    //defer fmt.Println("我最后才执行")  //先注册,等函数执行完成后,逆序执行defer注册的代码
//    //defer fmt.Println("ddddd")
//    defer func() {        // 匿名函数
//        fmt.Println("我最后才执行")
//    }()
//    defer func() {
//        fmt.Println("我最后才执行")
//        //出异常
//        //这个代码执行不到了, 再执行之前的defer
//    }()
//
//    fmt.Println("111")
//    fmt.Println("222")
//    panic("我出错了")  //主动抛出异常
//    //var a []int =make([]int,2,3)
//    //fmt.Println(a[9])
//    fmt.Println("这句话还会执行吗 ?不会了")
//}

//在defer中恢复程序,继续执行
func main() {

    f1()
    f2()
    f3()
}
func f1()  {
    fmt.Println("f1")
}

func f2()  {
    defer func() {        // 异常处理模板
        //recover() //恢复程序继续执行f3
        if err:=recover();err!=nil{  // recover()有返回值为异常信息err 如果不为nil(空),表示出了异常
            fmt.Println(err)  //把异常信息打印出来
        }
    }()
    fmt.Println("f2")
    //如果这个地方出了异常
    panic("我出错了")    // 报错,执行上面的defer

    fmt.Println("永远执行不到")
}
func f3()  {
    fmt.Println("f3")
}

// python中
//print('ssss')
//try:
//    print('ssss')
//    raise("xxxx")
//    print('我永远不会执行')
//except Exception as e:
//    print(e)
//finally:
//    print('我永远会执行')

//go 中
//print('ssss')
//defer func() {       // 异常处理模板
//    if err:=recover();err!=nil{    
//        fmt.Println(err)
//    }
//    //finally 写在这
//    print('我永远会执行')
//}()
//print('ssss')
//panic("xxxx")
//print('我永远不会执行')

1 进程,线程,协程

# python中为什么让你开进程,由于cpython解释器,有GIL锁,同一时刻,只能有一个线程执行
# 开进程--》相当于开了多个线程,重新起了一个cpython解释器,运行代码