当前位置:首页 > 科技  > 软件

腾讯互娱面经详解

来源: 责编: 时间:2024-05-11 09:17:23 103观看
导读先来唠唠图片今天刷脉脉的时候, 发现百度副总裁璩静一个人竟然占了前三的两个热榜, 对于她的离职你怎么看?言归正传, 本文的重点还是分享面经干货今天分享的是一位朋友在腾讯互娱的面经, 他本人目前已经是收到offer了

先来唠唠

图片图片kDS28资讯网——每日最新资讯28at.com

今天刷脉脉的时候, 发现百度副总裁璩静一个人竟然占了前三的两个热榜, 对于她的离职你怎么看?kDS28资讯网——每日最新资讯28at.com

言归正传, 本文的重点还是分享面经干货kDS28资讯网——每日最新资讯28at.com

今天分享的是一位朋友在腾讯互娱的面经, 他本人目前已经是收到offer了, 让我们来看看这个难度如何:kDS28资讯网——每日最新资讯28at.com

图片图片kDS28资讯网——每日最新资讯28at.com

面试题详解

Go接口

接口在Golang中扮演着连接不同类型之间的桥梁,它定义了一组方法的集合,而不关心具体的实现。接口的作用主要体现在以下几个方面:kDS28资讯网——每日最新资讯28at.com

多态性:kDS28资讯网——每日最新资讯28at.com

接口允许不同的类型实现相同的方法,从而实现多态性。这意味着我们可以使用接口类型来处理不同的对象,而不需要关心具体的类型。kDS28资讯网——每日最新资讯28at.com

package mainimport "fmt"type Animal interface { Sound() string}type Dog struct{}func (d Dog) Sound() string { return "Woof!"}type Cat struct{}func (c Cat) Sound() string { return "Meow!"}func main() { animals := []Animal{Dog{}, Cat{}} for _, animal := range animals {  fmt.Println(animal.Sound()) }}

在上面的示例中,我们定义了一个Animal接口,它包含了一个Sound()方法。然后,我们实现了Dog和Cat两个结构体,分别实现了Sound()方法。通过将Dog和Cat类型赋值给Animal接口类型,我们可以在循环中调用Sound()方法,而不需要关心具体的类型。这就体现了接口的多态性,不同的类型可以实现相同的接口方法。kDS28资讯网——每日最新资讯28at.com

解耦合:kDS28资讯网——每日最新资讯28at.com

接口可以将抽象与实现分离,降低代码之间的耦合度。通过定义接口,我们可以将实现细节隐藏起来,只暴露必要的方法,从而提高代码的可维护性和可读性。kDS28资讯网——每日最新资讯28at.com

package mainimport "fmt"type Printer interface { Print(string)}type ConsolePrinter struct{}func (cp ConsolePrinter) Print(message string) { fmt.Println(message)}type FilePrinter struct{}func (fp FilePrinter) Print(message string) { // 将消息写入文件 fmt.Println("Writing message to file:", message)}func main() { printer := ConsolePrinter{} printer.Print("Hello, World!") printer = FilePrinter{} printer.Print("Hello, World!")}

在上面的示例中,我们定义了一个Printer接口,它包含了一个Print()方法。然后,我们实现了ConsolePrinter和FilePrinter两个结构体,分别实现了Print()方法。通过将不同的结构体赋值给Printer接口类型的变量,我们可以在主函数中调用Print()方法,而不需要关心具体的实现。这样,我们可以根据需要轻松地切换不同的打印方式,实现了解耦合。kDS28资讯网——每日最新资讯28at.com

可扩展性:kDS28资讯网——每日最新资讯28at.com

package mainimport "fmt"type Shape interface { Area() float64}type Rectangle struct { Width  float64 Height float64}func (r Rectangle) Area() float64 { return r.Width * r.Height}type Circle struct { Radius float64}func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius}func main() { shapes := []Shape{Rectangle{Width: 5, Height: 10}, Circle{Radius: 3}} for _, shape := range shapes {  fmt.Println("Area:", shape.Area()) }}

在上面的示例中,我们定义了一个Shape接口,它包含了一个Area()方法。然后,我们实现了Rectangle和Circle两个结构体,分别实现了Area()方法。通过将不同的结构体赋值给Shape接口类型的切片,我们可以在循环中调用Area()方法,而不需要关心具体的类型。这样,当我们需要添加新的形状时,只需要实现Shape接口的Area()方法即可,而不需要修改已有的代码。这就实现了代码的可扩展性。kDS28资讯网——每日最新资讯28at.com

接口的应用场景

  1. API设计:接口在API设计中起到了至关重要的作用。通过定义接口,我们可以规范API的输入和输出,提高代码的可读性和可维护性。
  2. 单元测试:接口在单元测试中也扮演着重要的角色。通过使用接口,我们可以轻松地替换被测试对象的实现,从而实现对被测代码的独立测试。
  3. 插件系统:接口可以用于实现插件系统,通过定义一组接口,不同的插件可以实现这些接口,并在程序运行时动态加载和使用插件。
  4. 依赖注入:接口在依赖注入中也有广泛的应用。通过定义接口,我们可以将依赖对象的创建和管理交给外部容器,从而实现松耦合的代码结构。

空结构体的用途

不包含任何字段的结构体,就叫做空结构体。kDS28资讯网——每日最新资讯28at.com

空结构体的特点:kDS28资讯网——每日最新资讯28at.com

  1. 零内存占用
  2. 地址都相同
  3. 无状态

空结构体的使用场景

  • 实现set集合

在 Go 语言中,虽然没有内置 Set 集合类型,但是我们可以利用 map 类型来实现一个 Set 集合。由于 map 的 key 具有唯一性,我们可以将元素存储为 key,而 value 没有实际作用,为了节省内存,我们可以使用空结构体作为 value 的值。kDS28资讯网——每日最新资讯28at.com

package mainimport "fmt"type Set[K comparable] map[K]struct{}func (s Set[K]) Add(val K) {   s[val] = struct{}{}}func (s Set[K]) Remove(val K) {   delete(s, val)}func (s Set[K]) Contains(val K) bool {   _, ok := s[val]   return ok}func main() {   set := Set[string]{}   set.Add("程序员")   fmt.Println(set.Contains("程序员")) // true   set.Remove("程序员")   fmt.Println(set.Contains("程序员")) // false}
  • 用于通道信号

空结构体常用于 Goroutine 之间的信号传递,尤其是不关心通道中传递的具体数据,只需要一个触发信号时。例如,我们可以使用空结构体通道来通知一个 Goroutine 停止工作:kDS28资讯网——每日最新资讯28at.com

package main    import (     "fmt"     "time"  )    func main() {     quit := make(chan struct{})     go func() {        // 模拟工作        fmt.Println("工作中...")        time.Sleep(3 * time.Second)        // 关闭退出信号        close(quit)   }()       // 阻塞,等待退出信号被关闭     <-quit     fmt.Println("已收到退出信号,退出中...")  }
  • 作为方法接收器

有时候我们需要创建一组方法集的实现(一般来说是实现一个接口),但并不需要在这个实现中存储任何数据,这种情况下,我们可以使用空结构体来实现:kDS28资讯网——每日最新资讯28at.com

type Person interface {   SayHello()   Sleep()}type CMY struct{}func (c CMY) SayHello() {   fmt.Println("你好,世界。")}func (c CMY) Sleep() {   fmt.Println("晚安,世界...")}

Go原生支持默认参数或可选参数吗,如何实现

什么是默认参数

默认参数是指在函数调用时,如果没有提供某个参数的值,那么使用函数定义中指定的默认值。这种语言特性可以减少代码量,简化函数的使用。kDS28资讯网——每日最新资讯28at.com

在Go语言中,函数不支持默认参数。这意味着如果我们想要设置默认值,那么就需要手动在函数内部进行处理。kDS28资讯网——每日最新资讯28at.com

例如,下面是一个函数用于计算两个整数的和:kDS28资讯网——每日最新资讯28at.com

func Add(a int, b int) int {       return a + b}

如果我们希望b参数有一个默认值,例如为0,那么可以在函数内部进行处理:kDS28资讯网——每日最新资讯28at.com

func AddWithDefault(a int, b int) int {       if b == 0 {           b = 0    }    return a + b}

上面的代码中,如果b参数没有提供值,那么默认为0。通过这种方式,我们就实现了函数的默认参数功能。kDS28资讯网——每日最新资讯28at.com

需要注意的是,这种处理方式虽然可以实现默认参数的效果,但会增加代码复杂度和维护难度,因此在Go语言中不被推荐使用。kDS28资讯网——每日最新资讯28at.com

什么是可选参数

可选参数是指在函数调用时,可以省略一些参数的值,从而让函数更加灵活。这种语言特性可以让函数更加易用,提高代码的可读性。kDS28资讯网——每日最新资讯28at.com

在Go语言中,函数同样不支持可选参数。但是,我们可以使用可变参数来模拟可选参数的效果。kDS28资讯网——每日最新资讯28at.com

下面是一个函数用于计算任意个整数的和:kDS28资讯网——每日最新资讯28at.com

func Add(nums ...int) int {       sum := 0    for _, num := range nums {           sum += num    }    return sum}

上面的代码中,我们使用...int类型的可变参数来接收任意个整数,并在函数内部进行求和处理。kDS28资讯网——每日最新资讯28at.com

如果我们希望b和c参数为可选参数,那么可以将它们放到nums可变参数之后:kDS28资讯网——每日最新资讯28at.com

func AddWithOptional(a int, nums ...int) int {       sum := a    for _, num := range nums {           sum += num    }    return sum}

上面的代码中,我们首先将a参数赋值给sum变量,然后对可变参数进行求和处理。如果函数调用时省略了nums参数,则sum等于a的值。kDS28资讯网——每日最新资讯28at.com

需要注意的是,使用可变参数模拟可选参数的效果虽然能够实现函数的灵活性,但也会降低代码的可读性和规范性。因此在Go语言中不被推荐使用。kDS28资讯网——每日最新资讯28at.com

defer执行顺序

在 Go 中,defer 语句用于延迟(defer)函数的执行,通常用于在函数执行结束前执行一些清理或收尾工作。当函数中存在多个 defer 语句时,它们的执行顺序是“后进先出”(Last In First Out,LIFO)的,即最后一个被延迟的函数最先执行,倒数第二个被延迟的函数次之,以此类推。kDS28资讯网——每日最新资讯28at.com

在 Go 中,defer 语句中的函数在执行时会被压入一个栈中,当函数执行结束时,这些被延迟的函数会按照后进先出的顺序执行。这意味着在函数中的 defer 语句中的函数会在函数执行结束前执行,包括在 return 语句之前执行。kDS28资讯网——每日最新资讯28at.com

协程之间信息如何同步

协程(Goroutine)之间的信息同步通常通过通道(Channel)来实现。通道是 Go 语言中用于协程之间通信的重要机制,可以安全地在不同协程之间传递数据,实现协程之间的信息同步。kDS28资讯网——每日最新资讯28at.com

一些常见的方法来实现协程之间的信息同步:kDS28资讯网——每日最新资讯28at.com

  1. 使用无缓冲通道:无缓冲通道是一种同步通道,发送和接收操作会在数据准备好之前被阻塞。通过无缓冲通道,可以实现协程之间的同步通信,确保数据的正确传递。
  2. 使用带缓冲通道:带缓冲通道允许在通道中存储一定数量的元素,发送操作只有在通道已满时才会被阻塞。通过带缓冲通道,可以实现异步通信。
  3. 使用同步原语:Go 语言中的 sync 包提供了一些同步原语,如互斥锁(Mutex)、读写锁(RWMutex)等,可以用于协程之间的同步访问共享资源。
  4. 使用select语句:select 语句可以用于在多个通道操作中选择一个执行,可以实现协程之间的多路复用和超时控制。
  5. 使用context包:context 包提供了一种在协程之间传递取消信号和截止时间的机制,可以用于协程之间的协调和同步。

GMP模型

GM模型开销大的原因?

最开始的是GM模型没有 P 的,是M:N的两级线程模型,但是会出现一些性能问题:kDS28资讯网——每日最新资讯28at.com

  • 全局队列的锁竞争。M从全局队列中添加或获取 G 的时候,都是需要上锁的(下图执行步骤要加锁),这样就会导致锁竞争,虽然达到了并发安全,但是性能是非常差的。
  • M 转移 G 会有额外开销。M 在执行 G 的时候,假设 M1 执行的 G1 创建了 G2,新创建的就要放到全局队列中去,但是这时有一个空闲的 M2 获取到了 G2,那么这样 G1、G2 会被不同的 M 执行,但是 M1 中本来就有 G2 的信息,M2 在 G1 上执行是更好的,而且取和放到全局队列也会来回加锁,这样都会有一部分开销。
  • 线程的使用效率不能最大化。M 拿不到的时候就会一直空闲,阻塞的时候也不会切换。也就是没有 workStealing 机制和 handOff 机制。

kDS28资讯网——每日最新资讯28at.com

转载本文请联系「王中阳Go」公众号。kDS28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-87961-0.html腾讯互娱面经详解

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 这是一篇给Java初学者看的JVM文章

下一篇: 异步编程在C#中的应用:深入理解Task

标签:
  • 热门焦点
Top
Baidu
map