go 笔记

目录

目录

1、基本类型

2、方法相关

3、接口

4、recover

 5、类的创建

6、进程状态

7、进程的调度

8、go的多线程模型

 9、channel

 发送接收​编辑

 10、select

11、for -select 的相关配置

12、timer 包

13、锁

 sync只执行一次

sync waitGroup 

14、切片相关

1、基本类型

2、方法相关

3、接口

4、recover

 5、类的创建

私有类

6、进程状态

 

7、进程的调度

  • 线程是cpu调度的基本单位,进程是资源占有的基本单位。
  • 因为线程中的代码是在用户态运行,而线程的调度是在内核态,所以线程切换会触发用户态和内核态的切换。
  • 线程上下文切换的代价是高昂的:上下文切换的延迟取决于不同的因素,大概是50到100 ns左右,考虑到硬件平均在每个核心上每ns执行12条指令,那么一次上下文切换可能会花费600到1200条指令的延迟时间。

cpu上下文

CPU 寄存器,是CPU内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此叫做CPU上下文

它是指,先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。

多线程和协程

虽然I/O线程数量增多,会造成非常频繁的上下文切换,进而影响效率。但在互联网应用中,它却是一个优秀的解决方案。

更优秀的解决方式也有,那就是使用协程。协程是用户态的线程,是对普通线程更细粒度的划分。它是在用户态运行的,由用户自行调度,所以也就避免了频繁的上下文切换问题。

但协程在Java中还不成熟,它依然是Golang语言的诱人特性。使用Golang开发的Web服务,可以采用更少的线程来支持大量I/O密集型的请求。

三者的区别:

线程进程协程
资源分配在进程内共享资源在进程间独立分配资源在单个进程内共享资源
执行方式并发执行抢占性执行并发执行
系统开销较小较大较小

Go协程的调度是由Go语言运行时(runtime)自动管理的,而不是由操作系统内核调度的。这意味着,Go协程可以在不切换内核线程的情况下切换,从而避免了线程切换带来的开销。

其次,Go协程是协作式多任务,而不是抢占式多任务。这意味着,在Go语言中,协程之间是通过让出时间片的方式来协作的,而不是通过抢占处理器的方式。这使得Go协程在多个协程之间切换时,更加高效。

最后,Go协程是非常轻量级的。在Go语言中,创建一个新的协程只需要2kb左右的的内存空间,而传统的线程通常需要几兆的内存空间。这使得Go协程可以创建成千上万个,而不会对系统带来太大的负担。

但是,Go协程也有一些缺点,由于Go协程是由Go语言运行时自动调度的,因此它们之间并没有固定的执行关系,也就是说,不能保证某个协程在另一个协程之前执行。因此,在使用Go协程时,需要注意避免出现竞态条件的情况。

 

 

8、go的多线程模型

 9、channel

不同于其他编程语言,Go使用通信通道(channel)来传递数据。这使得只有一个协程(goroutine)能够访问数据,避免了竞态条件的出现。

 

 

 发送接收

 

 10、select

11、for -select 的相关配置

12、timer 包

func main() {
	//timer 和 channel
	timer := time.NewTimer(5 * time.Second)
	fmt.Printf("time is %v", time.Now().Unix())
	expireTime := <-timer.C
	fmt.Printf("expireTime is %v \n", expireTime)

	fmt.Printf("expireTime is %v", timer.Stop())

	timer2 := <-time.After(5 * time.Second)
	fmt.Printf("timer2 is %v \n", timer2)

	//简单的定时器
	intChan := make(chan int, 1)
	go func() {
		time.Sleep(1 * time.Second)
		intChan <- 1
	}()

	select {
	case <-intChan:
		fmt.Printf("1s Done")
	case <-time.NewTimer(3 * time.Second).C:
		fmt.Println("time out")
	}

}

	//每间隔5s发送一个随机数
	intChan2 := make(chan int, 1)
	timeTicker := time.NewTicker(5 * time.Second)
	go func() {
		for _ = range timeTicker.C {
			select {
			case intChan2 <- 1:
			case intChan2 <- 2:
			case intChan2 <- 3:
			}
		}
	}()

	var sum int
	//遍历这个channel
	for e := range intChan2 {
		fmt.Println("reveice ", e)
		sum += e
		if sum > 10 {
			fmt.Println("超过10 任务结束")
			//结束定时timer
			timeTicker.Stop()
			break
		}
	}

13、锁

 

 读写锁

 sync只执行一次

sync waitGroup 

14、切片相关

首先强调几点关于切片的注意事项

  1. Golang 中的函数参数,都是传值,不是传地址
  1. 对于切片自身的底层数据结构,我们可以通过索引的方式拿到底层数组的地址,并修改其地址上的值,例如 sli[2] = "hello",这是可以直接修改
  1. 如果传入的切片,期望实参也能够被改变的话,那么就需要想办法修改切片的底层数组

    1. 通过传切片的地址,也就是传指针的方式
    2. 在函数中,去索引切片的底层数组地址,进行修改数据
	x := []int{1, 2, 3}
	func(arr *[]int) {
		(*arr)[0] = 7
		fmt.Println(arr)
	}(&x)
	fmt.Println(x)

map和slice,在golang中,当map作为形参时,虽然是值传递,但是由于make()返回的是一个指针类型,所以我们可以在函数哪修改map的数值并影响到函数外

严格来说golang没有引用传递,下面示例汇总虽然在exchangeSlice内对切片的修改反馈到了main函数中,但是内部和外部的slice的地址其实是不一样的。两个slice的地址不一致,但是他们指向了同一个底层数组,在exchangeSlice函数内的变更最后反馈到了底层数组上。

package main

import "fmt"

func main() {
	slice := []int{1,2,3,4,5}
	fmt.Println(slice)
	exchangeSlice(slice)
	fmt.Println(slice)
}

func exchangeSlice(slice []int) {
	for k, v := range slice {
		slice[k] = v * 2
	}
}

但是需要注意

这里需要格外注意,在内部exchangeSlice函数内如果发生了底层数组的扩容,那么exchangeSlice内的slice则会申请新的数组,在扩容后的slice做的变更也将不会再反馈到外部main函数的slice中

slice在append的时候,如果底层数组的大小(cap)不够了,就会发生扩容。发生扩容的时候,slice结构体的指针会指向一个新的底层数组,然后把原来数组中的元素拷贝到新数组中,最后添加上append的新元素,就完成了扩容。 所以在这个时候,函数内部slice的改变是不会影响到函数外部slice的。因为此时,两个结构体中的指针指向的底层数组已经不相同了。

package main

import "fmt"


func main() {
	slice := []int{1,2,3}
	fmt.Println(slice)
	exchangeSlice(slice)
	fmt.Println(slice)
}

func exchangeSlice(slice []int) {
	slice = append(slice, 4, 5, 6); // 原始数组大小为4,超过4会扩容
	for k, v := range slice {
		slice[k] = v * 2
	}
	fmt.Println(slice)
}

slice扩容 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值