最新golang语言面试题总结(一)

1、go中make和new区别

new:

  1. 分配内存。内存里存的值是对应类型的零值。
  2. 只有一个参数。参数是分配的内存空间所存储的变量类型,Go语言里的任何类型都可以是new的参数,比如int, 数组,结构体,甚至函数类型都可以。
  3. 返回的是指针。

make:

  1. 分配和初始化内存。
  2. 只能用于slice, map和chan这3个类型,不能用于其它类型。如果是用于slice类型,make函数的第2个参数表示slice的长度,这个参数必须给值。
  3. 返回的是原始类型,也就是slice, map和chan,不是返回指向slice, map和chan的指针。

2、进程、线程、协程区别

进程:一个具有特定功能的程序运行在一个数据集上的一次动态过程。是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。(QQ、微信等等)

线程:一个进程可以有多个线程,每个线程会共享父进程的资源(创建线程开销占用比进程小很多,可创建的数量也会很多),有时被称为轻量级进程(Lightwight Process,LWP),是操作系统调度(CPU调度)执行的最小单位。

协程:一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。                                                                                                                

3、go中channel底层是什么

什么是channel?

chan是Go中的一种特殊类型,不同的协程可以通过channel来进行数据交互。

channel分为有缓冲区与无缓冲区两种

channel底层?

 channel是基于环形队列实现的。

type hchan struct {
	qcount   uint           // 队列中的总数据
	dataqsiz uint           // 循环队列的大小
	buf      unsafe.Pointer // 指向环形队列的元素数组
	elemsize uint16
	closed   uint32
	elemtype *_type // 元素类型
	sendx    uint   // 发送指数
	recvx    uint   // 收到指数
	recvq    waitq  // 接收列表
	sendq    waitq  // 发生列表

	// lock protects all fields in hchan, as well as several
	// fields in sudogs blocked on this channel.
	//
	// Do not change another G's status while holding this lock
	// (in particular, do not ready a G), as this can deadlock
	// with stack shrinking.
	lock mutex
}

图:未完待补充

4、go derfer执行顺序

 1、多个defer的执行顺序为“后进先出”;

 2、defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

我们知道defer后面一定要接一个函数的,所以defer的数据结构跟一般函数类似,也有栈地址、程序计数器、函数地址等等。
与函数不同的一点是它含有一个指针,可用于指向另一个defer,每个goroutine数据结构中实际上也有一个defer指针,该指针指向一个defer的单链表,每次声明一个defer时就将defer插入到单链表表头,每次执行defer时就从单链表表头取出一个defer执行。

5、go如何用两个协程交替打印出123456

协程1打印基数,协程2打印偶数,用一个channel实现
代码如下:
package main

import (
	"fmt"
	"sync"
)

//打印奇数
func PrintOddNumber(wg *sync.WaitGroup, ch chan int, num int) {
	defer wg.Done()
	for i := 0; i <= num; i++ {
		ch <- i
		if i%2 != 0 {
			fmt.Println("奇数:", i)
		}
	}
}

//打印偶数
func PrintEvenNumber(wg *sync.WaitGroup, ch chan int, num int) {
	defer wg.Done()
	for i := 1; i <= num; i++ {
		<-ch
		if i%2 == 0 {
			fmt.Println("偶数:", i)
		}
	}
}
func main() {
	wg := sync.WaitGroup{}
	ch := make(chan int)
	wg.Add(1)
	go PrintOddNumber(&wg, ch, 10)
	go PrintEvenNumber(&wg, ch, 10)
	wg.Wait()
}
结果:
奇数: 1
偶数: 2
奇数: 3
偶数: 4
奇数: 5
偶数: 6
奇数: 7
偶数: 8
奇数: 9
偶数: 10

 6、go中slice和数组的区别

 1. 数组定长,定义的时候就需要确定。切片长度不定,append时会自动扩容

2. 相同大小数组可以赋值,会拷贝全部内容。slice赋值和指针一样。数组和slice之间不能相互赋值。当然slice有自己的copy函数

3. 数组也可以进行切片,返回值是一个slice,改变slice时会同步修改数组内容,相当于取得了这个数组的指针

7、go中channel在你们项目中使用的场景举例

1、消息传递、消息过滤

2、信号广播

3、事件订阅与广播

4、请求、响应转发

5、任务分发

8、go中使用chan要注意什么 

1、当需要不断从channel读取数据时,最好使用for-range读取channel,这样既安全又便利,当channel关闭时,for循环会自动退出,无需主动监测channel是否关闭,可以防止读取已经关闭的channel,造成读到数据为通道所存储的数据类型的零值。 

例如:for x := range ch {
		//
	}

2、读已关闭的channel会造成零值 ,如果不确定channel,需要使用ok进行检测。

if v, ok := <-chan;ok{
		///
	}

3、select可以同时监控多个通道的情况,只处理未阻塞的case。当通道为nil时,对应的case永远为阻塞,无论读写。特殊关注:普通情况下,对nil的通道写操作是要panic的

select {
		case <-ch:
		//
		default:
		//
	}

4、使代码更易读、更易维护,防止只读协程对通道进行写数据,但通道已关闭,造成panic。

5、有缓冲通道是异步的,无缓冲通道是同步的,有缓冲通道可供多个协程同时处理,在一定程度可提高并发性。

6、使用selecttime.After,看操作和定时器哪个先返回,处理先完成的,就达到了超时控制的效果

func WorkWithOutTime(t time.Duration) (int, error) {
	select {
	case ret := <-Work():
		return ret, nil
	case <-time.After(t):
		return 0, errors.New("timeout")
	}
}
func Work() <-chan int {
	ch := make(chan int)
	go func() {
		//
	}()
	return ch
}

7、是为操作加上超时的扩展,这里的操作是channel的读或写

func OnlyRead(ch chan int) {
	select {
	case <-ch:
	//
	default:
		//
	}
}

8、channel本质上传递的是数据的拷贝,拷贝的数据越小传输效率越高,传递结构体指针,比传递结构体更高效

9、说一下go中的CSP

CSP(communicating sequential processes)并发模型:Go语言特有且推荐使用的。

Go的CSP并发模型,是通过goroutine和channel来实现的。

不同于传统的多线程通过共享内存来通信,CSP讲究的是“以通信的方式来共享内存”。

普通的线程并发模型,就是像Java、C++、或者Python,他们线程间通信都是通过共享内存的方式来进行的。非常典型的方式就是,在访问共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过锁来访问,因此,在很多时候,衍生出一种方便操作的数据结构,叫做“线程安全的数据结构”。

Go的CSP并发模型,是通过goroutine和channel来实现的。

goroutine 是Go语言中并发的执行单位。可以理解为用户空间的线程。
channel是Go语言中不同goroutine之间的通信机制,即各个goroutine之间通信的”管道“,有点类似于Linux中的管道。 

 

M:是内核线程

P : 是调度协调,用于协调M和G的执行,内核线程只有拿到了 P才能对goroutine继续调度执行,一般都是通过限定P的个数来控制golang的并发度

G : 是待执行的goroutine,包含这个goroutine的栈空间

Gn : 灰色背景的Gn 是已经挂起的goroutine,它们被添加到了执行队列中,然后需要等待网络IO的goroutine,当P通过 epoll查询到特定的fd的时候,会重新调度起对应的,正在挂起的goroutine。

Golang为了调度的公平性,在调度器加入了steal working 算法 ,在一个P自己的执行队列,处理完之后,它会先到全局的执行队列中偷G进行处理,如果没有的话,再会到其他P的执行队列中抢G来进行处理
csp很好的例子:生产者和消费者。

10、说一下go中channel是不是线程安全的,什么情况下会变成线程不安全

 Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication),Channel也可以理解是一个先进先出的队列,通过管道进行通信。

Golang的Channel,发送一个数据到Channel 和 从Channel接收一个数据 都是

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值