go:接口、并发与协程、信道、缓冲信道、mutex、异常处理
接口
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,就是错误对象 } 都会执行 }() 写代码写代码写代码写代码写代码写代码 */
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?