go的基础

go

数组

数组是值拷贝
两个初始化

// 数组 值拷贝
func TestArray(t *testing.T) {
	var myArray [3]int
	for i := range myArray {
		fmt.Println(myArray[i])
	}
	fmt.Println("===================")
	myArrary2 := [3]int{1, 2}
	for i := range myArrary2 {
		fmt.Println(myArrary2[i])
	}
}

切片(动态数组)

func edit(array []int) {
	//引用传递 切片
	array[0] = 2
}
// 动态数组 切片
func TestDongtai(t *testing.T) {
	myArray := []int{1, 2, 3} //动态数组
	for _, value := range myArray {
		//1 2 3
		fmt.Println(value)
	}
	//切片传递的是指针
	edit(myArray)
	println("================")
	for _, value := range myArray {
		// 2 2 3
		fmt.Println(value)
	}
}
func TestSclice(t *testing.T) {
	//声明slice是一个切片并初始化
	slice := []int{1, 2, 3}
	//声明slicel1是一个切片,但是并没有分配空间
	var slicel1 []int
	slicel1[1] = 1 //这样编译会报错,因为没有分配空间

	slicel1 = make([]int, 3) //分配3空间 默认值是0
	slicel1[1] = 1           //不会报错
	//声明一个切片,并且分配空间
	slice2 := make([]int, 3)
	fmt.Println("结果", len(slice), len(slicel1), len(slice2))
	//判断slice是不是一个空
	// slice == nil
}

// 切片的追加
func TestCap(t *testing.T) {
	//长3 容量5
	var number = make([]int, 3, 5)
	fmt.Printf("len = %d, cap = %d\n slice = %v\n", len(number), cap(number), number)
	number = append(number, 1)
	fmt.Printf("len = %d, cap = %d\n slice = %v\n", len(number), cap(number), number)
	//cap如果已经满了 你再添加一个元素 切片会给我们增加一个cap容量,相当于cap是我们开辟空间的一个单位
	//不定义cap就会len一样长

}

什么是值拷贝?什么是引用传递?

值拷贝就是传递过去的是一个数据副本,对它修改原本的数据并不会被修改,而引用传递,传的是地址,就可以直接修改到这个数据

长度和容量分别代表什么?

var number = make([]int, 3, 5)
就上面这个代码,切片的长度为3,容量为5。
初始化这个number的切片的容量为5,初始化了3,值为 [0,0,0]
当我们往里面添加数据的时候
number = append(number, 1)
再次查看number的长度和容量,分别为4,5
当我们再插入两个数据时,再次查看长度和容量时:
就会变成6,10。可以看出容量是切片每次扩容的单位。

map

// 传参也是引用传参
func TestMap(t *testing.T) {
	//声明一个map 但是现在是一个空map
	var m map[string]interface{}
	//给map开辟一个空间
	m = make(map[string]interface{})
	m["key"] = "value"
	map1 := map[string]string{
		"key": "value",
	}
	fmt.Println(map1)
	delete(map1, "key")
}

封装

声明一种行的数据类型myint,是int的一个别名

type Hero struct {
	Name  string
	Ad    int
	Level int
}

func (h Hero) GetName() string {
	return h.Name
}

func (h Hero) SetName(name string) {
	h.Name = name
}

func (h Hero) show() {
	fmt.Println(h)
}

func TestStruct(t *testing.T) {
	h := Hero{"zjx", 1, 2}
	h.show()
	h.SetName("tyq")
	h.show()
}

结果
这样是无法修改值的,所以应该传指针

type Hero struct {
	Name  string
	Ad    int
	Level int
}

func (h *Hero) GetName() string {
	return h.Name
}

func (h *Hero) SetName(name string) {
	h.Name = name
}

func (h *Hero) show() {
	fmt.Println(h)
}

func TestStruct(t *testing.T) {
	h := &Hero{"zjx", 1, 2}
	h.show()
	h.SetName("tyq")
	h.show()
}

结果

继承

type Humer struct {
	Id int
	Name string
	Age int
}

type u struct {
	Habby string
	Humer
}

多态

type Animal interface {
	Sleep()
	GetColor() string // 获取动物的颜色
}
// 具体的类
type Cat struct {
	color string // 猫的颜色
}
// 必须要全部实现接口
func (cat *Cat) Sleep() {
	fmt.Println("cat is sleep")
}
func (cat *Cat) GetColor() string {
	fmt.Println("cat is GetColor")
	return cat.color
}
// 具体的类
// 具体的类
type Dog struct {
	color string // 猫的颜色
}
// 必须要全部实现接口
func (dog *Dog) Sleep() {
	fmt.Println("dog is sleep")
}
func (dog *Dog) GetColor() string {
	fmt.Println("dog is GetColor")
	return dog.color
}
func TestInterface(t *testing.T) {
	var animal Animal
	animal = &Cat{color: "red"}
	animal.Sleep() // 猫的sleep
	animal = &Dog{color: "green"}
	animal.Sleep()
}

interface 类型断言

type Reader interface {
	ReadBook()
}
type Writer interface {
	WriteBook()
}
// 具体类型
type Book struct {
}
func (b *Book) ReadBook() {
	fmt.Println("Read a book.")
}
func (b *Book) WriteBook() {
	fmt.Println("Write a book.")
}
func TestPair(t *testing.T) {
	b := &Book{}
	var r Reader
	r = b
	r.ReadBook()
	var w Writer
	w = r.(Writer) //类型断言 r是否实现了writer这个接口 pair不变
	w.WriteBook()
}

反射

pair

变量的结构
变量的结构
pairtype:int:11,value
pair不会改变

type User struct {
	Id   int
	Name string
	Age  int
}

func (u User) Call() {
	fmt.Println("user call ...")
}
func TestReflectNum(t *testing.T) {
	//var num float64 = 1.234
	user := User{1, "zjx", 12}
	reflectNum(user)
}

// 反射
func reflectNum(arg interface{}) {
	argType := reflect.TypeOf(arg)
	argValue := reflect.ValueOf(arg)
	fmt.Println(argType, argType.Name())
	fmt.Println(argValue)
	fmt.Println("==============")
	//通过type获取里面的字段
	for i := 0; i < argType.NumField(); i++ {
		field := argType.Field(i)
		value := argValue.Field(i).Interface()
		fmt.Println(field, value)
	}
	fmt.Println("==========method")
	fmt.Println(argType.NumMethod())
	for i := 0; i < argType.NumMethod(); i++ {
		m := argType.Method(i)
		fmt.Println(m.Name, m.Type)
	}
}

结构体标签

在这里插入图片描述

结构体标签应用

json编解码 序列化和反序列化
orm映射关系

goroutine

协程并发
协程:coroutine,轻量级线程

进程和线程的弊端

  • 1.高消耗调度cpu

进程线程的数量越多,切换的成本就越大,也就越浪费【切换上下文对cpu的消耗】
多线程也会有争夺锁的问题

  • 2.高内存占用

进程占用内存4GB (32bit os)
线程 4MB

我们可以让cpu无感 把一个内核线分为内核空间和用户空间,也就是在用户级区操作协程,用调度器来操作协程。也就是说把调度器做的越好,协程的效率也就更好
在这里插入图片描述一个线程中可以有任意多个协程,但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。
go天生就支持协程,是因为go的底层做了协程调度器的转换。

golong对协程的处理

  1. 修改名字为goroutine
  2. 内存改为几kb,可以大量并发
  3. 灵活调度,也就是可以随意切换

golong的调度器处理

早期的调度器了只有一个全局的go协程队列
哪个需要就去拿,并且要获取锁释放锁,还会造成锁竞争。
如果这个协程创建了新的协程,可能回转移给其他线程来使用,就会导致局部性

GMP

改进之后
加粗样式
内核就是M 用户就是G 管理器就是p
在这里插入图片描述
一个p同时只能执行一个go
一个时间能够同行的go的数量就是p的数量

调度器的设计策略

复用线程

Work stealing机制(偷取机制):
全局队列里放的是放不下的G
在这里插入图片描述
Hand off机制(分离机制):
m1正在执行g1,m2即将执行g3,m1的g1阻塞了,就会创建/唤醒新的m3,把和m1绑定的p转换到m3,不耽误g2。如果g1还要工作就加入到其他m中,否则m1就睡眠或者销毁

在这里插入图片描述

利用并行

在这里插入图片描述

抢占

在这里插入图片描述

全局G队列(根据work stealing机制作为补充)

如果队列都没有g了,就去全局队列偷,每次偷也是需要加锁释放锁的机制,从而也会浪费时间

channel

channal有同步两个不同go的能力
一个协成在管道中取不到值就会阻塞 等待管道里面的值写 因为无缓冲的 (没设置情况)

基本使用

func TestChannl(t *testing.T) {
	c := make(chan int, 3) //有缓存
	go func() {
		defer fmt.Println("goroutine exit")

		for i := 0; i < cap(c); i++ {
			c <- i
			fmt.Println("goroutine send:", i)
			fmt.Println("len:", len(c), "cap:", cap(c))
		}
	}()

	for i := 0; i < cap(c); i++ {
		fmt.Println("main等待===========")

		fmt.Println(<-c) //接收打出来
	}
	fmt.Println("结束")
}

无缓冲

无缓存
在第 1 步,两个 goroutine 都到达通道,但哪个都没有开始执⾏发送或者接收。
在第 2 步,左侧的 goroutine 将它的⼿伸进了通道,这模拟了向通道发送数据的⾏
为。这时,这个 goroutine 会在通道中被锁住,直到交换完成。
在第 3 步,右侧的 goroutine 将它的⼿放⼊通道,这模拟了从通道⾥接收数据。这
个 goroutine ⼀样也会在通道中被锁住,直到交换完成。
在第 4 步和第 5 步,进⾏交换,并最终,在第 6 步,两个 goroutine 都将它们的
⼿从通道⾥拿出来,这模拟了被锁住的 goroutine 得到释放。两个 goroutine 现在
都可以去做其他事情了。

有缓冲

有缓存在第 1 步,右侧的 goroutine 正在从通道接收⼀个值。
在第 2 步,右侧的这个 goroutine独⽴完成了接收值的动作,⽽左侧的
goroutine 正在发送⼀个新值到通道⾥。
在第 3 步,左侧的goroutine 还在向通道发送新值,⽽右侧的 goroutine 正在
从通道接收另外⼀个值。这个步骤⾥的两个操作既不是同步的,也不会互相阻
塞。
最后,在第 4 步,所有的发送和接收都完成,⽽通道⾥还有⼏个值,也有⼀些空
间可以存更多的值。
特点:
当channel已经满,再向⾥⾯写数据,就会阻塞
当channel为空,从⾥⾯取数据也会阻塞

关闭channel

在这里插入图片描述
channel不像⽂件⼀样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结
束range循环之类的,才去关闭channel;
关闭channel后,⽆法向channel 再发送数据(引发 panic 错误后导致接收⽴即返回零值);
关闭channel后,可以继续从channel接收数据;
对于nil channel,⽆论收发都会被阻塞。

select

单流程下⼀个go只能监控⼀个channel的状态,select可以完成
监控多个channel的状态
select具备多路channel的监控状态功能
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值