1 接口 、2 并发与协程 、3 信道,缓冲信道 、4 mutex 、5 异常处理

发布时间 2023-05-05 16:00:13作者: DYuH

1 接口

# 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
}

2 并发与协程

# 并发:一段时间内,执行多个任务的能力
# 并行:同一时刻,执行多个任务的能力  (必须有多核支持)

# 实现并发
	-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("我是主协程,任务执行完了")

}

3 信道,缓冲信道

# 多个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,继续往后执行
*/

4 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)
}

5 异常处理

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,就是错误对象
	}
	都会执行
}()
写代码写代码写代码写代码写代码写代码

*/