golang工程师面试第三天

yinshidaoshi · 2018-01-15 21:36:48 · 10824 次点击 · 预计阅读时间 2 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2018-01-15 21:36:48 的文章,其中的信息可能已经有所发展或是发生改变。

1.简述golang中make和new的区别

make用于内建类型(只能用于创建map、slice 和channel)的内存分配。并且返回一个有初始值(非零)的T类型,而不是*T。

new用于各种类型的内存分配。new(T)分配了零值填充的T类型的内存空间,并且返回其地址,即一个T类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型T的零值。有一点非常重要:*new返回指针

2.简要描述go中的main和init函数的区别 首先,这两个函数应用位置不同,init函数可以应用于所有的package,main只能应用于 package main,需要注意的是虽然一个package中可以写任意多个init,但是无论是从可读性还是可维护性来说,都是不推荐的;

其次,这两个函数定义时都不能有任何的参数和返回值,

最后,个人理解,init函数为初始化操作,main函数为程序入口。 一图胜千言

image.png

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

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
}

解题思路:pase_map返回了一个key为string,值为*student的map,遍历结果应为map的key和value,主要误区在m中,value的值都为&{lei 14},这也是我所不能理解的,感觉进了思维误区,求大神指点

4.通道选择器中的panic问题

package main

import "fmt"

var c1 = make(chan int,1)
var c2 = make(chan string,1)

func main() {

    c1 <-1
    c2 <-"hello"

    select {

    case v1 := <-c1:
        fmt.Println(v1)

    case v2 := <-c2:
        panic(v2)


    }
}

不一定会引起恐慌,可能会打印1,也可能会恐慌

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

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)
}

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

10824 次点击  ∙  5 赞  
加入收藏 微博
20 回复  |  直到 2019-09-24 13:54:51
lyp256
lyp256 · #1 · 7年之前

关于第3题问题,有一个误区:

TIM截图20180116060554.png

m[v.Name]=&v这里是把 for range 中v 的地址赋值给m。而在range迭代中 v是分配的一个地址(每次迭代中的v都是一个变量),然后每一次迭代给v赋不同的值,所以map所有的值都指向了 v的地址,而v在for range 迭代中只保留了最后一次迭代的值&{lei 14}(前面的值都被覆盖了),因此value 都是&{lei 14}

下面是证明实验,添加打印了地址语句

26.png

实验结果:

3.png

dalizitajiu
dalizitajiu · #2 · 7年之前

第5题这个是随机输出的,这道题怎么解?

callmedonsir
callmedonsir · #3 · 7年之前
dalizitajiudalizitajiu #2 回复

第5题这个是随机输出的,这道题怎么解?

你可以百度一下runtime.Gosched()就知道怎么做了

victorl
victorl · #4 · 7年之前

协程交替执行,使其能顺序输出1-20的自然数,应该这样写才合理吧

package main

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)

    exit := make(chan int)

    go func() {
        defer close(ch2)
        for i := 1; i <= 20; i++ {
            println("g1:", <-ch1)
            i++
            ch2 <- i
        }
    }()

    go func() {
        defer close(ch1)
        for i := 0; i < 20; i++ {
            i++
            ch1 <- i
            println("g2:", <-ch2)
        }
        close(exit)
    }()

    <-exit
}
luominggao123
luominggao123 · #5 · 7年之前

#############利用单核,通道交互.

package main

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

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

    for i := 1; i <= 10; i++ {
        go func(i int) {
            <-b
            fmt.Println(2 * i)
            a <- i
        }(i)
    }
    a <- 1
    time.Sleep(4 * time.Second)
}
victorl
victorl · #6 · 7年之前

第五题 一个channel传值就够了

package main

func main() {
    ch := make(chan int)
    exit := make(chan int)

    go func() {
        for i := 1; i <= 20; i++ {
            println("g1:", <-ch)
            i++
            ch <- i
        }
    }()

    go func() {
        defer func() {
            close(ch)
            close(exit)
        }()
        for i := 0; i < 20; i++ {
            i++
            ch <- i
            println("g2:", <-ch)
        }
    }()

    <-exit
}
l_sivan
l_sivan · #7 · 7年之前
package main

import (
    "fmt"
)

func main() {
    c := make(chan int)
    exit := make(chan struct{})
    go func() {
        for i := 1; i <= 20; i++ {
            select {
            case c <-i :
                break
            case j := <-c:
                fmt.Println(j)
            }
        }
    }()
    go func() {
        defer func() {
            close(c)
            close(exit)
        }()
        for i := 1; i <= 20; i++ {
            select {
            case j := <-c:
                fmt.Println(j)
            case c <-i :
                break
            }
        }
    }()
    <-exit
}
dalizitajiu
dalizitajiu · #8 · 7年之前
callmedonsircallmedonsir #3 回复

#2楼 @dalizitajiu 你可以百度一下runtime.Gosched()就知道怎么做了

谢谢,不过感觉下面老哥的答案也挺好的

dalizitajiu
dalizitajiu · #9 · 7年之前
victorlvictorl #4 回复

协程交替执行,使其能顺序输出1-20的自然数,应该这样写才合理吧 ```go package main func main() { ch1 := make(chan int) ch2 := make(chan int) exit := make(chan int) go func() { defer close(ch2) for i := 1; i <= 20; i++ { println("g1:", <-ch1) i++ ch2 <- i } }() go func() { defer close(ch1) for i := 0; i < 20; i++ { i++ ch1 <- i println("g2:", <-ch2) } close(exit) }() <-exit } ```

老哥,稳

yinshidaoshi
yinshidaoshi · #10 · 7年之前
callmedonsircallmedonsir #3 回复

#2楼 @dalizitajiu 你可以百度一下runtime.Gosched()就知道怎么做了

我刚才百度了一下runtime.Gosched(),功能是运行到这一行时就会让出时间片,

package main

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

func main() {

    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(3 * time.Second)

}

}

按理说,运行到1的时候就会让出时间片,紧接着运行下面线程,运行到2时让出时间片再运行到1. 但是事实并非如此,运行结果还是随机的

xux9311
xux9311 · #11 · 7年之前

下面的代码为什么不行,输出经常是错的。不明白,求各位大佬解释一下

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    for i := 1; i <= 20; i++ {
        go func() {
            fmt.Println(<-ch)
        }()
        ch <- i
    }
}
yinshidaoshi
yinshidaoshi · #12 · 7年之前
xux9311xux9311 #11 回复

下面的代码为什么不行,输出经常是错的。不明白,求各位大佬解释一下 ``` package main import ( "fmt" ) func main() { ch := make(chan int) for i := 1; i <= 20; i++ { go func() { fmt.Println(<-ch) }() ch <- i } } ```

    ch := make(chan int)

    for i := 1; i <= 20; i++ {
        go func() {
            ch <- i
        }()

        fmt.Println(<-ch)
    }
zibu
zibu · #13 · 7年之前
xux9311xux9311 #11 回复

下面的代码为什么不行,输出经常是错的。不明白,求各位大佬解释一下 ``` package main import ( "fmt" ) func main() { ch := make(chan int) for i := 1; i <= 20; i++ { go func() { fmt.Println(<-ch) }() ch <- i } } ```

这个问题 是go的协程调度器产生的 当主协程往通道里写入数据时,此时子协程1(即会输出1的协程)读出数据1,但没有输出1,就失去了CPU的时间片。 主协程继续创建了子协程2(即会输出2的协程),子协程2读到数据。 在极端情况下,可能连续创建了20个子协程,然后groutine调度器按一定顺序调度这20个协程,自然会出现乱序。 但实际上会在个别协程上出现偶尔的乱序。比如2在1前面输出,实际上是子协程1先于子协程2创建。然后子协程2先于子线程1获得运行 这个问题是比较难以看出的,也很难用语言描述。

killernova
killernova · #14 · 7年之前
yinshidaoshiyinshidaoshi #12 回复

#11楼 @xux9311 ```go ch := make(chan int) for i := 1; i <= 20; i++ { go func() { ch <- i }() fmt.Println(<-ch) } ```

你这个不符合题意中的:交替两字。

wangjh0629
wangjh0629 · #15 · 7年之前
xux9311xux9311 #11 回复

下面的代码为什么不行,输出经常是错的。不明白,求各位大佬解释一下 ``` package main import ( "fmt" ) func main() { ch := make(chan int) for i := 1; i <= 20; i++ { go func() { fmt.Println(<-ch) }() ch <- i } } ```

for循环并没有被阻塞,所以会不停的创建go协程,等待输出

qqqasdwx
qqqasdwx · #16 · 7年之前
package main

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

func main() {
    ch1:=make(chan int)
    ch2:=make(chan int)
    runtime.GOMAXPROCS(1)
    for i:=1;i<=10;i++ {
        go func(i int) {
            <-ch1
            fmt.Println(2*i-1)
            ch2<-i
        }(i)
    }
    time.Sleep(1*time.Second)
    for i:=1;i<=10;i++ {
        go func(i int) {
            <-ch2
            fmt.Println(2*i)
            ch1<-i
        }(i)
    }
    ch1<-1
    time.Sleep(3*time.Second)
}
paulyung541
paulyung541 · #17 · 6年之前
lyp256lyp256 #1 回复

关于第3题问题,有一个误区: ![TIM截图20180116060554.png](https://static.studygolang.com/180116/19d1cda62812b08654e95b2c6b08f986.png) `m[v.Name]=&v`这里是把 for range 中v 的地址赋值给m。而在range迭代中 v是分配的一个地址(每次迭代中的v都是一个变量),然后每一次迭代给v赋不同的值,所以map所有的值都指向了 v的地址,而v在for range 迭代中只保留了最后一次迭代的值&{lei 14}(前面的值都被覆盖了),因此value 都是&{lei 14} 下面是证明实验,添加打印了地址语句 ![26.png](https://static.studygolang.com/180116/859fe0eaf1a320400c463c528f9d13d6.png) 实验结果: ![3.png](https://static.studygolang.com/180116/03caf3ec1e3c252f73a98a048bdee480.png)

v是这个函数的一个临时变量,函数运行完之后这个地址可能会挪作他用,那么这样访问是有可能出错的吧

13346185563
13346185563 · #18 · 6年之前
ch1 := make(chan int)
ch2 := make(chan int)
exit := make(chan int)

var wg sync.WaitGroup
wg.Add(2)
go func() {
    for i := 1; i <= 20; i++ {
        println("g1:", <-ch1)
        i++
        ch2 <- i
    }
}()

go func() {
    for i := 0; i < 20; i++ {
        i++
        ch1 <- i
        println("g2:", <-ch2)
    }
    exit <- 1
}()
wg.Wait()
close(exit)

}

jiaxiake
jiaxiake · #19 · 6年之前

func main(){ p := make(chan int,1) v := make(chan int,1) ch := make(chan int) p <- 1 go func(){ for i := 0 ; i <= 20 ; i+=2{ <-p fmt.Println("F",i) v<-1 } close(ch) }() go func(){ for i := 1 ; i <= 20 ; i+=2{ <-v fmt.Println("s",i) p<-1 } }()

fmt.Println("end")
<-ch
fmt.Println("end")

}

kevingolang
kevingolang · #20 · 6年之前
1334618556313346185563 #18 回复

ch1 := make(chan int) ch2 := make(chan int) exit := make(chan int) var wg sync.WaitGroup wg.Add(2) go func() { for i := 1; i <= 20; i++ { println("g1:", <-ch1) i++ ch2 <- i } }() go func() { for i := 0; i < 20; i++ { i++ ch1 <- i println("g2:", <-ch2) } exit <- 1 }() wg.Wait() close(exit) }

  8 func main() {
  9     ch1 := make(chan int)
 10     ch2 := make(chan int)
 11 
 12     var wg sync.WaitGroup
 13     wg.Add(2)
 14     go func() {
 15         for i := 1; i <= 20; i++ {
 16             fmt.Println("g1:", <-ch1)
 17             i++
 18             ch2 <- i
 19         }
 20         wg.Done()                                                                                                                                                   
 21     }()
 22 
 23     go func() {
 24         for i := 0; i < 20; i++ {
 25             i++
 26             ch1 <- i
 27             fmt.Println("g2:", <-ch2)
 28         }
 29         wg.Done()
 30     }()
 31     wg.Wait()
 32 }

漏了wg.Done()

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传