坑:
- uint 不能直接相减,结果是负数会变成一个很大的uint
- channel一定记得close(可以不用关闭 chan。但这前提是这个 chan 永远不再使用了!)
- goroutine记得return或者中断,不然容易造成goroutine占用大量CPU
- 从slice创建slice,注意原slice的操作可能导致底层数组变化
- channel是通过注册相关goroutine id实现消息通知的
- slice底层是数组,保存了len,capacity和对数组的引用
- JSON 标准库对 nil slice 和 空 slice 的处理是不一致
面试题:
1.除了 mutex 以外还有那些方式安全读写共享变量?
- Golang中Goroutine 可以通过 Channel 进行安全读写共享变量。
2.无缓冲 Chan 的发送和接收是否同步?
- ch := make(chan int)
- 无缓冲的channel由于没有缓冲发送和接收需要同步.
- channel无缓冲时,发送阻塞直到数据被接收,接收阻塞直到读到数据。
- ch := make(chan int, 2)
- 有缓冲channel不要求发送和接收操作同步.
- channel有缓冲时,当缓冲满时发送阻塞,当缓冲空时接收阻塞。
3.Golang 中常用的并发模型?
- 通过channel通知实现并发控制
- 通过sync包中的WaitGroup实现并发控制
- 在Go 1.7 以后引进的强大的Context上下文,实现并发控制
4. 什么是goroutine,他与process, thread有什么区别?
process进程:
- 程序运行时的产物,也就是正在运行的代码;
- 拥有独享的虚拟地址空间(堆和栈),由操作系统调度。
thread线程:
- 系统级线程,总是在进程之内;
- 拥有独立的栈空间,所有运行在同一个进程里的线程,共享该进程的虚拟地址空间;
- 一个进程至少有一个线程,每个进程的第一个线程(主线程)随着进程启动而创建;
- 主线程外的系统级线程由代码显示创建和销毁,调度由操作系统完成;
协程:
- 用户级线程;
- 协程架设于系统级线程之上,所以和系统级线程一样不共享栈空间,共享所在进程的虚拟地址空间;
- 创建、销毁、调度、状态变更以及其中的代码和数据都需要在程序中手动地实现和处理,程序编写者既是指令下达者又是指令执行者。
goroutine:
- go语言特有的概念,也是一种用户级线程;
- 不共享栈空间,共享所在进程的虚拟地址空间,依据不同的操作系统和go语言版本,每个goroutine初始栈空间为2到8KB;
- go语言的运行时系统(runtime)提供调度器,在语言层面自动调度用户级线程,不提供手动管理,并对接KSE(Kernal Space Entity,内核调度实体),也就是系统级线程。
5. 什么是channel,为什么它可以做到线程安全?
Channel是Go中的一个核心类型,可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication),Channel也可以理解是一个先进先出的队列,通过管道进行通信。
Golang的Channel,发送一个数据到Channel 和 从Channel接收一个数据 都是 原子性的。而且Go的设计思想就是:不要通过共享内存来通信,而是通过通信来共享内存,前者就是传统的加锁,后者就是Channel。也就是说,设计Channel的主要目的就是在多任务间传递数据的,这当然是安全的。
6. 了解读写锁吗,原理是什么样的,为什么可以做到?
7. 如何用channel实现一个令牌桶?
8. 如何调试一个go程序?
9. 如何写单元测试和基准测试?
10. goroutine 的调度是怎样的?
11. golang 的内存回收是如何做到的?
12. 什么是interface?
13.Go 空结构体 struct{} 的使用
- 不占据任何的内存空间。
- map 作为集合(Set)使用时,可以将值类型定义为空结构体,仅作为占位符使用即可
- 不发送数据的信道(channel)
- 仅包含方法的结构体