接口
1.实现多个接口
2.接口嵌套
3.接口零值
package main
import "fmt"
// 接口
//1 实现多个接口 具体类型可以赋值给多种接口类型
//type Animal interface {
// run()
//}
//
//type Dog interface {
// eat()
//}
//
//type MuYangDog struct {
// name string
// age int
//}
//
//func (d *MuYangDog) eat() {
// fmt.Println(d.name, "在吃东西")
//}
//func (d *MuYangDog) run() {
// fmt.Println(d.name, "在跑")
//}
//func main() {
// //var d Dog = &MuYangDog{name: "牧羊犬", age: 6}
// //d.eat()
//
// var a Animal = &MuYangDog{name: "牧羊犬", age: 6}
// a.run()
//}
// 2 接口嵌套
//type Animal interface {
// run()
//}
//
//type Dog interface {
// eat()
// Animal // 就等同于把Animal中的所有方法放在这里了
//}
//type MuYangDog struct {
// name string
// age int
//}
//
//func (d *MuYangDog) eat() {
// fmt.Println(d.name, "在吃东西")
//
//}
//
//func (d *MuYangDog) run() {
// fmt.Println(d.name, "在跑")
//
//}
//func main() {
//
// var d Animal = &MuYangDog{name: "牧羊犬"}
// d.run()
//}
// 2 接口零值---->引用类型
type Dog interface {
eat()
}
type MuYangDog struct {
name string
age int
}
func (d *MuYangDog) eat() {
fmt.Println(d.name, "在吃东西")
}
func main() {
var d Dog
fmt.Println(d) // nil
}
并发与协程
# 并发:一段时间内,执行多个任务的能力
# 并行:同一时刻,执行多个任务的能力 (必须有多核支持)
# 实现并发
1 开启多进程(耗费资源)
2 开启多线程
3 开启协程
# 正常语言,就是开启多线程,实现并发,python由于GIL锁的存在,同一时刻只能有一个线程在执行
python中计算密集型(耗费cpu)----》开多进程
pyhton中IO密集型-----》开启多线程
其他语言,只要开启了多线程,就能利用多核优势
go语言,进程,线程都不需要,开启协程
我们认为:开启多个协程,因为在一个线程下,也不能利用多核优势
所以go内置了,go的协程不是普通协程,它是 线程池+协程
GMP模型
G:go中开启一个协程叫 goroutine
P:Processer 处理器(相当于程序层面的线程)
M:thread 操作系统线程
package main
import (
"fmt"
"time"
)
// 协程
func task(i int) {
fmt.Println("我是任务,我开始干活了", i)
time.Sleep(2 * time.Second)
fmt.Println("我是任务,干完活了", i)
}
func main() {
fmt.Println("我是主协程")
//// 执行任务,同步执行
//task()
// 执行任务,异步执行 ,开启了一个go协程
//go task()
//go task()
//go task()
//go task()
for i := 0; i < 10; i++ {
go task(i)
}
time.Sleep(3 * time.Second)
fmt.Println("我是主协程,任务执行完了")
}
信道、缓冲信道
# 多个goroutine之间通信
-可以通过共享变量--->mutex保证并发安全
-信道,通道 channel
# 不要通过共享内存来通信,而应该通过通信来共享内存
package main
// 信道 一种类型,定义成变量,可以多个协程之间使用
//func main() {
//1 定义信道--->没有初始化,零值 是nil,说明引用类型,传递到函数中,不需要取地址
//var c chan int
//fmt.Println(c) // nil
// 2 定义并初始化
//var c chan int = make(chan int)
//fmt.Println(c) // 0xc00005c060 内存地址,引用
// 3 初始化后,放值,取值
// 放值
//c <- 1 // 把数字1 放入到信道c 中 //deadlock
//c <- 2
//c <- 3
// 取值
//var i int=<-c
//var i =<-c
//i := <-c //从信道中取出一个值
//fmt.Println(i)
// 4 信道默认,放值取值都是阻塞的,多个协程,一个协程放,另一个协程取值,才不会死锁
// var c = make(chan int)
// fmt.Println("主goroutine开始起来")
// go sendEmail(c) // 把信道变量,传入协程函数中
//
// //<-c // 等待从c中取值 取出来,不要了
// i := <-c // 等待从c中取值
// fmt.Println("主goroutine结束了", i)
//
//}
//
//func sendEmail(c chan int) {
// fmt.Println("发邮件了")
// time.Sleep(3 * time.Second)
// fmt.Println("邮件发完了")
// c <- 1 // 发完邮件了,放1进去
//
//}
// 5 单向信道 (只能放或只能取
//func main() {
// var c = make(chan bool)
// fmt.Println(<-c)
//}
//
//// func task3(c <-chan bool) { // 只读信道
//func task3(c chan<- bool) { // 只写信道
// time.Sleep(2 * time.Second)
// //c <- true
// //<-c
//
//}
// 6 关闭信道 ,关闭后,不能再放值,取值了 close 内置函数关闭
//
//func task4(c chan int) {
// time.Sleep(2 * time.Second)
// c <- 1
// close(c) // 信道关闭
//}
//
//func main() {
// var c = make(chan int)
//
// go task4(c)
// for {
// v, ok := <-c
// if ok {
// fmt.Println("信道没关闭,取出来了")
// fmt.Println(v)
// } else {
// fmt.Println("信道被关了,结束死循环")
// break
// }
// }
//}
// 6.1 稍微复杂的信道关闭
//func task4(c chan int) {
// for i := 0; i < 10; i++ {
// c <- i
// }
//
// close(c) // 信道关闭
//}
//
//func main() {
// var c = make(chan int)
//
// go task4(c)
// for {
// v, ok := <-c
// if ok {
// fmt.Println("信道没关闭,取出来了")
// fmt.Println(v)
// } else {
// fmt.Println("信道被关了,结束死循环")
// break
// }
// }
//}
// 6.2 上面是for死循环,可以使用range循环
//func task4(c chan int) {
// for i := 0; i < 10; i++ {
// c <- i
// }
//
// close(c) // 信道关闭
//}
//
//func main() {
// var c = make(chan int)
//
// go task4(c)
// for i := range c { //只要信道关闭了,循环就结束了
// fmt.Println(i)
// }
//}
package main
import (
"fmt"
"sync"
)
//WaitGroup:等待其他协程执行完成
// 1 通过信道实现,等待所有任务执行完成
//func task5(c chan int, i int) {
// fmt.Println("第", i, "个协程执行")
// c <- i
//}
//
//func main() {
// var c = make(chan int, 5)
// for i := 0; i < 5; i++ {
// go task5(c, i)
// }
//
// // 等都执行完才结束
// for i := 0; i < 5; i++ {
// <-c
// }
// fmt.Println("主协程结束了")
//}
// 2 通过 等待所有协程执行完成
func task5(wg *sync.WaitGroup, i int) {
fmt.Println("第", i, "个协程执行")
wg.Done() // 这个任务完成,就写一个Done
}
func main() {
var wg sync.WaitGroup // 值类型 没有初始化
//fmt.Println(wg)
wg.Add(5)
for i := 0; i < 5; i++ {
//wg.Add(1) // 执行5次
go task5(&wg, i) //一定要取地址
}
wg.Wait()
fmt.Println("主协程结束了")
}
/*
WaitGroup:等待其他协程执行完成
用法:定义 值类型
wg.Add(1) 开启一个协程,就要加1
wg.Done() 一个协程结束,就Done一次,其实就是减一
wg.Wait() 等待计数器为0,继续往后执行
*/
mutex
# go 可以通过共享变量,实现通信
package main
import (
"fmt"
"sync"
)
var x = 0
func add(wg *sync.WaitGroup, lock *sync.Mutex) {
// 临界区要加锁
lock.Lock()
x = x + 1
lock.Unlock()
wg.Done()
}
func main() {
var w sync.WaitGroup
var lock sync.Mutex // 值类型
//fmt.Println(lock)
for i := 0; i < 1000; i++ {
w.Add(1)
go add(&w, &lock)
}
w.Wait()
fmt.Println("final value of x", x)
}
异常处理
package main
import "fmt"
// 异常处理 go 没有try语法,使用panic,defer,recover实现异常处理
func main() {
// 1 panic 内置函数,主动抛出异常, raise
//fmt.Println("111")
//panic("我错了")
//fmt.Println(222)
// 3 defer 先注册后调用,即便出了异常,也会执行
// 2 recover 内置函数,回复程序,继续执行
//defer func() {
// recover() // 恢复程序
//}()
//fmt.Println("111")
//panic("我错了")
//fmt.Println(222)
//
// 4 正常的go异常处理
test()
}
func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("出错了", err)
}
// 这是finally执行的地址
//fmt.Println("文论是否出错,我都会执行")
}()
fmt.Println("test执行了")
//panic("报错了")
//var s []int
//fmt.Println(s[9])
fmt.Println("test结束了")
}
// 其他语言
/*
try:
写代码
except Except as e:
出错了
finally:
都会执行
*/
// go
/*
defer func() {
if err := recover(); err != nil {
出错了,err就是e,就是错误对象
}
都会执行
}()
写代码写代码写代码写代码写代码写代码
*/