ShowMeBug初级Golang面试

Golang面试

Posted by kzdgt on Wednesday, July 27, 2022

new和make的区别

  • 二者都是用来做内存分配的。
  • make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身为这三种类型就是引用类型,所以就没有必要返回他们的指针了;
  • 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

了解golang的内存管理吗

Go 的内存是自动管理的,我们可以随意定义变量直接使用,不需要考虑变量背后的内存申请和释放的问题。

Golang 的内存管理本质上就是一个内存池,只不过内部做了很多的优化。比如自动伸缩内存池大小,合理的切割内存块等等。Golang 的程序在启动之初,会一次性从操作系统那里申请一大块内存作为内存池。这块内存空间会放在一个叫 mheap 的 struct 中管理,mheap 负责将这一整块内存切割成不同的区域,并将其中一部分的内存切割成合适的大小,分配给用户使用。

(待补充)

调用方函数传入结构体,应该传值还是指针

一般会根据实际情况。struct一般使用指针,因为可以节省资源,传值会发生自拷贝。还有比如说这个函数需要对原结构体进行一些修改。如果修改不希望影响上层调用则会考虑传值。

方法和接收者,什么时候应该使用指针类型接收者

  1. 需要修改接收者中的值
  2. 接收者是拷贝代价比较大的大对象
  3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

defer执行时机

  • 在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:

微信截图_20220727224603

  • defer 的执⾏顺序是先进后出。出现panic语句的时候,会先按照 defer 的后进先出顺序执⾏,最后 才会执⾏panic

操作系统中Linux线程有几种模型

这里以Linux为例。Linux历史上,最开始使用的线程是LinuxThreads,但LinuxThreads有些方面受限于内核的特性,从而违背了SUSV3 Pthreads标准。即它要根据内核的特性来实现线程,有些地方没有遵循统一的标准。后来IBM开发了NGPT(Next Generation POSIX Threads),性能明显优于LinuxThreads,人们曾把它当作LinuxThreads的继任者。但最后,又有一个项目NPTL(Native POSIX Threads Library)出来后,性能更优于NGPT。2002年NGPT项目停止开发,我们现在用的Linux线程就是NPTL。

1.多对一(M:1)的用户级线程模型

2.一对一(1:1)的内核级线程模型

3.多对多(M:N)的两级线程模型

上面的x对y(x:y)即x个用户线程对应y个内核调度实体(Kernel Scheduling Entity,这个是内核分配CPU的对象单位)。

LinuxThreads和NPTL都是采用一对一的线程模型,NGPT采用的是多对多的线程模型!!!

二.多对一用户线级程模型

多对一线程模型中,线程的创建、调度、同步的所有细节全部由进程的用户空间线程库来处理。用户态线程的很多操作对内核来说都是透明的,因为不需要内核来接管,这意味不需要内核态和用户态频繁切换。线程的创建、调度、同步处理速度非常快。当然线程的一些其他操作还是要经过内核,如IO读写。这样导致了一个问题:当多线程并发执行时,如果其中一个线程执行IO操作时,内核接管这个操作,如果IO阻塞,用户态的其他线程都会被阻塞,因为这些线程都对应同一个内核调度实体。在多处理器机器上,内核不知道用户态有这些线程,无法把它们调度到其他处理器,也无法通过优先级来调度。这对线程的使用是没有意义的!

三.一对一内核极线程模型

一对一模型中,每个用户线程都对应各自的内核调度实体。内核会对每个线程进行调度,可以调度到其他处理器上面。当然由内核来调度的结果就是:线程的每次操作会在用户态和内核态切换。另外,内核为每个线程都映射调度实体,如果系统出现大量线程,会对系统性能有影响。但该模型的实用性还是高于多对一的线程模型。

四.多对多两极线程模型

多对多模型中,结合了1:1和M:1的优点,避免了它们的缺点。每个线程可以拥有多个调度实体,也可以多个线程对应一个调度实体。听起来好像非常完美,但线程的调度需要由内核态和用户态一起来实现。可想而知,多个对象操作一个东西时,肯定要一些其他的同步机制。用户态和内核态的分工合作导致实现该模型非常复杂。NPTL曾经也想使用该模型,但它太复杂,要对内核进行大范围改动,所以还是采用了一对一的模型!!!

串行、并发与并行

串行:我们都是先读小学,小学毕业后再读初中,读完初中再读高中。

并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天)。

并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天)。

进程、线程和协程

进程(process):程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。

线程(thread):操作系统基于进程开启的轻量级进程,是操作系统调度执行的最小单位。

协程(coroutine):非操作系统提供而是由用户自行创建和控制的用户态‘线程’,比线程更轻量级。

并发模型

业界将如何实现并发编程总结归纳为各式各样的并发模型,常见的并发模型有以下几种:

  • 线程&锁模型
  • Actor模型
  • CSP模型
  • Fork&Join模型

Go语言中的并发程序主要是通过基于CSP(communicating sequential processes)的goroutine和channel来实现,当然也支持使用传统的多线程共享内存的并发方式。

golang协程/goroutine的原理

Goroutine 是 Go 语言支持并发的核心,在一个Go程序中同时创建成百上千个goroutine是非常普遍的,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB,并且 goroutine 的栈不是固定的,可以根据需要动态地增大或缩小, Go 的 runtime 会自动为 goroutine 分配合适的栈空间。区别于操作系统线程由系统内核进行调度, goroutine 是由Go运行时(runtime)负责调度。例如Go运行时会智能地将 m个goroutine 合理地分配给n个操作系统线程,实现类似m:n的调度机制,不再需要Go开发者自行在代码层面维护一个线程池。

Goroutine 是 Go 程序中最基本的并发执行单元。每一个 Go 程序都至少包含一个 goroutine——main goroutine,当 Go 程序启动时它会自动创建。

进程、线程、协程

区别

通信

gin创建路由的方式

「真诚赞赏,手留余香」

kzdgt Blog

真诚赞赏,手留余香

使用微信扫描二维码完成支付