golang面试准备(代码篇)

简介

网上找了几份golang面试代码看一下,总结一下

声明

本文所涉及到的题目来自网络,由本文作者收集整理

正文

  1. 写出下面代码的输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package main

    import (
    "fmt"
    )

    type student struct {
    Name string
    Age int
    }

    func main() {

    m := pase_map()

    for k, v := range m {
    fmt.Printf("key = %s,value =%v\n", k, v)
    }

    }

    func pase_map() map[string]*student {

    m := make(map[string]*student)

    stu := []student{{"joy", 12}, {"lei", 14}}

    for _, v := range stu {

    m[v.Name] = &v
    }
    return m
    }

    执行结果

    1
    2
    key = joy,value =&{lei 14}
    key = lei,value =&{lei 14}

    原因:for range使用副本v遍历mapm,&v指向同一个地址

  2. 协程交替执行,使其能顺序输出1-20的自然数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package main

    import (
    "fmt"
    "time"
    )

    func main() {

    for i:=1;i<=10;i++ {
    go func(i int) {
    fmt.Println(2*i-1)
    }(i)
    }

    for i:=1;i<=10;i++ {
    go func(i int) {
    fmt.Println(2*i)
    }(i)
    }

    time.Sleep(3*time.Second)
    }

    方法一:runtime.Gosched()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package main

    import (
    "fmt"
    "runtime"
    "time"
    )

    func main() {
    runtime.GOMAXPROCS(1)
    go func() {
    for i := 1; i <= 10; i++ {

    fmt.Println(2*i - 1)
    runtime.Gosched() //1
    }
    }()
    go func() {
    for i := 1; i <= 10; i++ {

    fmt.Println(2 * i)
    runtime.Gosched() //2
    }
    }()
    time.Sleep(1 * time.Second)

    }

    方法二:单核通道交互

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    package main

    import (
    "fmt"
    "runtime"
    "time"
    )

    func main() {
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 0)
    runtime.GOMAXPROCS(1)
    for i := 1; i <= 10; i++ {
    go func(i int) {
    <-ch1
    fmt.Println(2*i - 1)
    ch2 <- 0
    }(i)
    }

    for i := 1; i <= 10; i++ {
    go func(i int) {
    <-ch2
    fmt.Println(2 * i)
    ch1 <- 0
    }(i)
    }
    ch1 <- 1
    time.Sleep(1 * time.Second)
    }

    总结:比较倾向于使用方法二,因为没有改变原有程序结构

  3. 写出代码运行结果,如有错误则指出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package main

    import "fmt"

    func main() {
    defer func() {
    if err := recover(); err != nil {
    fmt.Print(err)
    } else {
    fmt.Print("no")
    }

    }()
    defer func() {
    panic("1111111111111")
    }()
    panic("22222222222")

    }

    执行顺序为:

    1
    2
    3
    1. panic("22222222222") panic向上传递
    2. panic("1111111111111") panic向上传递
    3. recover() 接收panic,打印最后一个panic

    所以结果为:

    1
    1111111111111
  1. select典型用法

    如果有多个case都可以运行,select会随机公平地选出一个执行,其他不会执行。
    如果没有可运行的case语句,且有default语句,那么就会执行default的动作。
    如果没有可运行的case语句,且没有default语句,select将阻塞,直到某个case通信可以运行

    • 超时判断
      比如在下面的场景中,使用全局resChan来接受response,如果时间超过3S,resChan中还没有数据返回,则第二条case将执行

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      var resChan = make(chan int)

      // do request
      func test() {
      select {
      case data := <-resChan:
      doData(data)
      case <-time.After(time.Second * 3):
      fmt.Println("request time out")
      }
      }

      func doData(data int) {
      //...
      }
    • 退出

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      //主线程(协程)中如下:
      var shouldQuit = make(chan struct{})

      func main() {
      {
      //loop
      }
      //...out of the loop
      select {
      case <-shouldQuit:
      cleanUp()
      return
      default:
      }
      //...
      }

      func cleanUp() {
      //clean something
      }

      //再另外一个协程中,如果运行遇到非法操作或不可处理的错误,就向shouldQuit发送数据通知程序停止运行
      close(shouldQuit)
    • 判断是否阻塞
      在某些情况下是存在不希望channel缓存满了的需求的,可以用如下方法判断

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      func main() {
      ch := make(chan int, 5)
      //...
      data := 0
      select {
      case ch <- data:
      default:
      //如果数据放不进去,就做相应操作,比如丢弃data。视需求而定
      }
      }
您的支持将鼓励我继续创作!