Golang面试前二夜准备:18-20题

简介: Golang面试前二夜准备:18-20题

640.png

Golang面试前二夜准备



题号 题目
18 怎么限制Goroutine的数量
19 Channel是同步的还是异步的
20 Goroutine和线程的区别


18. 怎么限制Goroutine的数量


在Golang中,Goroutine虽然很好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来。


所以我们可以限制下Goroutine的数量,这样就需要在每一次执行go之前判断goroutine的数量,如果数量超了,就要阻塞go的执行。


所以通常我们第一时间想到的就是使用带cap的通道(chan)。每次执行go func(负责消费chan)之前向通道写入值,直到通道满的时候就阻塞了,这样就相当于限制了goroutine的并发数。但是当main结束的时候,因为并不是所有的goroutine都执行完了,在main函数退出之后,这些goroutine就被强制结束了。这个时候我们就需要用到sync.WaitGroup。使用WaitGroup等待所有的goroutine退出。


package main
import (
 "fmt"
 "sync"
)
var ch chan  int
func elegance(wg *sync.WaitGroup){
 defer wg.Done()
 select {
 case ret := <- ch:
  fmt.Println(ret)
 }
}
func main(){
 ch = make(chan int,5) //控制并发
 wg := sync.WaitGroup{}
 for i:=0;i<10;i++{
  wg.Add(1)
  ch <-i //五个之后就会阻塞 除非有goroutine消费了chan 可以继续并发  但是上限是5
  go elegance(&wg)
 }
 wg.Wait()
 fmt.Println("end")
}


19. Channel是同步的还是异步的


Channel是异步进行的, channel存在3种状态:

  • nil,未初始化的状态,只进行了声明,或者手动赋值为nil
  • active,正常的channel,可读或者可写
  • closed,已关闭,千万不要误认为关闭channel后,channel的值是nil


下面我们对channel的三种操作解析:

  1. 零值(nil)通道;
  2. 非零值但已关闭的通道;
  3. 非零值并且尚未关闭的通道。


操作 一个零值nil通道 一个非零值但已关闭的通道 一个非零值且尚未关闭的通道
关闭 产生恐慌 产生恐慌 成功关闭
发送数据 永久阻塞 产生恐慌 阻塞或者成功发送
接收数据 永久阻塞 永不阻塞 阻塞或者成功接收


20. Goroutine和线程的区别


从调度上看,goroutine的调度开销远远小于线程调度开销


OS的线程由OS内核调度,每隔几毫秒,一个硬件时钟中断发到CPU,CPU调用一个调度器内核函数。这个函数暂停当前正在运行的线程,把他的寄存器信息保存到内存中,查看线程列表并决定接下来运行哪一个线程,再从内存中恢复线程的注册表信息,最后继续执行选中的线程。这种线程切换需要一个完整的上下文切换:即保存一个线程的状态到内存,再恢复另外一个线程的状态,最后更新调度器的数据结构。某种意义上,这种操作还是很慢的。


Go运行的时候包涵一个自己的调度器,这个调度器使用一个称为一个M:N调度技术,m个goroutine到n个os线程(可以用GOMAXPROCS来控制n的数量),Go的调度器不是由硬件时钟来定期触发的,而是由特定的go语言结构来触发的,他不需要切换到内核语境,所以调度一个goroutine比调度一个线程的成本低很多。


从栈空间上,goroutine的栈空间更加动态灵活


每个OS的线程都有一个固定大小的栈内存,通常是2MB,栈内存用于保存在其他函数调用期间哪些正在执行或者临时暂停的函数的局部变量。这个固定的栈大小,如果对于goroutine来说,可能是一种巨大的浪费。作为对比goroutine在生命周期开始只有一个很小的栈,典型情况是2KB, 在go程序中,一次创建十万左右的goroutine也不罕见(2KB*100,000=200MB)。而且goroutine的栈不是固定大小,它可以按需增大和缩小,最大限制可以到1GB。


goroutine没有一个特定的标识。


在大部分支持多线程的操作系统和编程语言中,线程有一个独特的标识,通常是一个整数或者指针,这个特性可以让我们构建一个线程的局部存储,本质是一个全局的map,以线程的标识作为键,这样每个线程可以独立使用这个map存储和获取值,不受其他线程干扰。


goroutine中没有可供程序员访问的标识,原因是一种纯函数的理念,不希望滥用线程局部存储导致一个不健康的超距作用,即函数的行为不仅取决于它的参数,还取决于运行它的线程标识。


相关文章
|
8月前
|
Go
golang力扣leetcode 面试题04.06.后继者
golang力扣leetcode 面试题04.06.后继者
56 0
|
8月前
|
Go
golang力扣leetcode 面试题01.05.一次编辑
golang力扣leetcode 面试题01.05.一次编辑
66 0
|
8月前
|
Java Go
Golang深入浅出之-Go语言指针面试必知:理解与使用指针
【4月更文挑战第21天】Go语言中的指针允许直接操作内存,常用于高效数据共享和传递。本文介绍了指针的基础知识,如声明、初始化和解引用,以及作为函数参数使用。此外,讨论了`new()`与`make()`的区别和内存逃逸分析。在结构体上下文中,指针用于减少复制开销和直接修改对象。理解指针与内存管理、结构体的关系及常见易错点,对于面试和编写高性能Go代码至关重要。
118 2
|
8月前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello(&quot;Alice&quot;)`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
107 1
|
8月前
|
存储 Go 开发者
Golang深入浅出之-Go语言字符串操作:常见函数与面试示例
【4月更文挑战第20天】Go语言字符串是不可变的字节序列,采用UTF-8编码。本文介绍了字符串基础,如拼接(`+`或`fmt.Sprintf()`)、长度与索引、切片、查找与替换(`strings`包)以及转换与修剪。常见问题包括字符串不可变性、UTF-8编码处理、切片与容量以及查找与替换的边界条件。通过理解和实践这些函数及注意事项,能提升Go语言编程能力。
238 0
|
8月前
|
监控 编译器 Linux
golang面试:golang的GPM调度模型(七)
golang面试:golang的GPM调度模型(七)
70 1
|
8月前
|
监控 安全 Go
golang面试:golang中的context(四)
golang面试:golang中的context(四)
86 0
|
5月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
2月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
2月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?

热门文章

最新文章