面试题笔试题学习日记——golang(7.25)

golang基础

关于协程,下面说法正确是()

A. 协程和线程都可以实现程序的并发执行
B. 线程比协程更轻量级
C. 协程不存在死锁问题
D. 通过channel来进行协程间的通信
参考答案:AD

struct结构体能不能比较?

  • 结构体不可以比较,但是同一类型的结构体的值可以比较是否相等(不可以比较大小);
    结构体所有字段的值都相等,两个结构体才相等;
    比较的两个结构体必须是相同类型才可以,也就是说他们字段的顺序、名称、类型、标签都相同才可以
  • 因为是强类型语言,所以不同类型的结构不能作比较,但是同一类型的实例值是可以比较的,实例不可以比较,因为是指针类型

defer

顺序:先注册后执行,后注册先执行(类似栈、LIFO后进先出法)
触发时机:包含defer的函数返回时
包含return的函数执行到末尾时
所在的goroutine发生panic时
更多相关defer的知识可以参考https://www.jianshu.com/p/79c029c0bd58

select

用法:select就是用来监听和channel有关的IO操作,当IO操作发生时,触发相应`的动作。类似linux中的select,io多路复用机制。可以用作协程的退出

//select基本用法
select {
case <- chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
}

注意事项:

  • 如果有一个或多个IO操作可以完成,则GO运行时系统会随机的选择一个执行,否则的话,如果有default分支,则执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行
  • 所有channel表达式都会被求值、所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右。
  • break关键字结束select

waitgroup和context

waitgroup和context都可以用来控制并发,waitgroup类似linux中的waitpid,使用如下:

func main() {
	//创建一个新的channel,输入值限制类型为int
	nochan := make(chan int)
	//指向waitgroup结构体
	waiter := &sync.WaitGroup{}
	//添加两个队列
	waiter.Add(2)
	//从channel里接收数据
	go func(ch chan int, wt *sync.WaitGroup) {
		data := <-ch
		fmt.Println("receive data ", data)
		wt.Done()
	}(nochan, waiter)
	//发送数据到channel
	go func(ch chan int, wt *sync.WaitGroup) {
		ch <- 5
		fmt.Println("send data ", 5)
		wt.Done()
	}(nochan, waiter)
	//先输出1,
	fmt.Println(1)
	//等待协程执行完
	waiter.Wait()
	//再输出2
	fmt.Println(2)
}

udi@udi:~/Udi_work_space/Udi_ws_go/ZipTest_backend$ go run main.go 
1
receive data  5
send data  5
2

context

  • context可以用来跟踪goroutine,比如有一个网络请求Request,每个Request都需要开启一个goroutine做一些事情,这些goroutine又可能会开启其他goroutine。这样的话,我们就可以通过Context,来跟踪这些goroutine,并且通过Context来控制他们的目的,这就是Go语言为我们提供的Context,中文可以称之为“上下文”。
  • 另外一个实际例子是,在Go服务器程序中,每个请求都会有一个goroutine去处理。然而,处理程序往往还需要创建额外的goroutine去访问后端资源,比如数据库、RPC服务等。由于这些goroutine都是在处理同一个请求,所以它们往往需要访问一些共享的资源,比如用户身份信息、认证token、请求截止时间等。而且如果请求超时或者被取消后,所有的goroutine都应该马上退出并且释放相关的资源。这种情况也需要用Context来为我们取消掉所有goroutine

生产者消费者模型实现

用channel实现生产者消费者模型

//生产者模型,往channel里输入数据
func produce(ch chan<- int) {
	for i := 0; i < 10; i++ {
		ch <- i
		fmt.Println("Send:", i)
	}
}
//消费者模型,从channel中取出数据
func consumer(ch <-chan int) {
	for i := 0; i < 10; i++ {
		v := <-ch
		fmt.Println("Receive:", v)
	}
}

ch = make(chan int) //无缓冲
ch = make(chan int) //有缓冲

用互斥锁和条件变量实现的生产者和消费者模型:

lock := new(sync.Mutex)
cond := sync.NewCond(lock)
var count int

func producer() {
	for i:=0;i<10;i++ {
		lock.Lock()
		count++
		cond.Signal()
		lock.Unlock()
	}
}

func consumer(){
	for i:=0;i<10;i++{
		lock.Lock()
		while(count == 0){
			cond.Wait()
		}
		count--
		lock.Unlock()
	}
}

new和make的区别

new的作用是初始化一个指向类型的指针(T),使用new函数来分配空间。传递给new函数的是一个类型,不是一个道,不是一个值。返回值是指向这个新分配的零值的指针。
make的作用是slice,map或chan初始化并返回引用(T)。
make(T,args)函数的目的与new(T)不同,它仅仅用于创建Slice,Map和Channel,并且返回类型是T(不是T)的一个初始化的(不是零值)的实例

go触发异常的场景

空指针解析,数组越界访问,除数为0,调用panic函数。

异常处理机制panic defer recover

panicing:F函数出现panic后,终止当前函数,并调用其defer函数,然后在终止调用F函数的函数并调用其defer函数,直至goroutine退出。
panic的处理:在defer函数中调用recover获得错误值,再处理错误。

函数变长参数使用

类型转换

type类型不能直接转换,只能强制转换,比如:

type Myint int

var a int
var b Myint

a = b // 错误
a = int(b) //正确

append,delete用法

append用来添加map中的元素
delete用来删除map中的元素

接口方法集调用规则

  • 类型T的可调用方法集包含接受者为T或者T的所有方法集
  • 类型T的可调用方法集包含接受者为T的所有方法
  • 类型T的可调用方法集不包含接受者为*T的方法

函数内变量在函数结束后并不释放,由垃圾回收器释放

猜你喜欢

转载自blog.csdn.net/qq_37109456/article/details/107579454
今日推荐