(上篇)

1. 写出下面代码输出内容

package main

import (
    "fmt"
)

func main() {
    defer_call()
}

func defer_call() {

    defer func() {
        fmt.Println("打印前")
    }()

    defer func() {
        fmt.Println("打印中")
    }()

    defer func() {
        fmt.Println("打印后")
    }()

    panic("触发异常")
}

触发异常deferpanicrecover
  • 案例一
package main

import (
    "fmt"
)

func main() {
    defer_call()
}

func defer_call() {

    defer func() {
        fmt.Println("打印前")
    }()

    defer func() {
        fmt.Println("打印中")
    }()

    defer func() { // 必须要先声明defer,否则recover()不能捕获到panic异常

        if err := recover();err != nil {
            fmt.Println(err) //err 就是panic传入的参数
        }
        fmt.Println("打印后")
    }()

    panic("触发异常")
}

输出内容为:

触发异常
打印后
打印中
打印前

Process finished with exit code 0

  • 案例二
package main

import (
    "fmt"
)

func main() {
    defer_call()
}

func defer_call() {

    defer func() {
        fmt.Println("打印前")
    }()

    defer func() { // 必须要先声明defer,否则recover()不能捕获到panic异常
        if err := recover();err != nil {
            fmt.Println(err) //err 就是panic传入的参数
        }
        fmt.Println("打印中")
    }()

    defer func() {

        fmt.Println("打印后")
    }()

    panic("触发异常")
}

输出内容为:

打印后
触发异常
打印中
打印前

Process finished with exit code 0

  • 案例三
package main

import (
    "fmt"
)

func main() {
    defer_call()
}

func defer_call() {

    defer func() {
        if err := recover();err != nil {
            fmt.Println(err) //err 就是panic传入的参数
        }
        fmt.Println("打印前")
    }()

    defer func() { // 必须要先声明defer,否则recover()不能捕获到panic异常
        if err := recover();err != nil {
            fmt.Println(err) //err 就是panic传入的参数
        }
        fmt.Println("打印中")
    }()

    defer func() {
        if err := recover();err != nil {
            fmt.Println(err) //err 就是panic传入的参数
        }
        fmt.Println("打印后")
    }()

    panic("触发异常")
}

输出内容为:

触发异常
打印后
打印中
打印前

Process finished with exit code 0

总结:

deferreturndeferLIFOGopanicdeferrecoverdeferrecover()

2. 以下代码有什么问题,说明原因

package main
import (
    "fmt"
)
type student struct {
    Name string
    Age  int
}
func pase_student() map[string]*student {
    m := make(map[string]*student)
    stus := []student{
        {Name: "zhou", Age: 24},
        {Name: "li", Age: 23},
        {Name: "wang", Age: 22},
    }
    for _, stu := range stus {
        m[stu.Name] = &stu
    }
    return m
}
func main() {
    students := pase_student()
    for k, v := range students {
        fmt.Printf("key=%s,value=%v \n", k, v)
    }
}

运行结果:

key=zhou,value=&{wang 22} 
key=li,value=&{wang 22} 
key=wang,value=&{wang 22} 

Process finished with exit code 0

修改一下代码:

将下面的代码:

for _, stu := range stus {
    m[stu.Name] = &stu
}

修改为:

for _, stu := range stus {
    fmt.Printf("%v\t%p\n",stu,&stu)
    m[stu.Name] = &stu
}

运行结果为:

{shen 24}   0xc4200a4020
{li 23} 0xc4200a4020
{wang 22}   0xc4200a4020
key=shen,value=&{wang 22} 
key=li,value=&{wang 22} 
key=wang,value=&{wang 22} 

Process finished with exit code 0

stustructm[stu.Name]=&stustruct

形同如下代码:

var stu student 
for _, stu = range stus {
    m[stu.Name] = &stu
} 

修正方案,取数组中原始值的地址:

for i, _ := range stus {
    stu:=stus[i]
    m[stu.Name] = &stu
}

重新运行,效果如下:

{shen 24}   0xc42000a060
{li 23} 0xc42000a0a0
{wang 22}   0xc42000a0e0
key=shen,value=&{shen 24} 
key=li,value=&{li 23} 
key=wang,value=&{wang 22} 

Process finished with exit code 0

3. 下面的代码会输出什么,并说明原因

package main

import (
    "fmt"
    "runtime"
    "sync"
)

func init() {
    fmt.Println("Current Go Version:", runtime.Version())
}
func main() {
    runtime.GOMAXPROCS(1)

    count := 10
    wg := sync.WaitGroup{}
    wg.Add(count * 2)
    for i := 0; i < count; i++ {
        go func() {
            fmt.Printf("[%d]", i)
            wg.Done()
        }()
    }
    for i := 0; i < count; i++ {
        go func(i int) {
            fmt.Printf("-%d-", i)
            wg.Done()
        }(i)
    }
    wg.Wait()
}

运行效果:

Current Go Version: go1.10.1
-9-[10][10][10][10][10][10][10][10][10][10]-0--1--2--3--4--5--6--7--8-
Process finished with exit code 0

forgo func
go funcifori=10go funci1010
go funciforiigo func

4. 下面代码会输出什么?

package main

import "fmt"

type People struct{}

func (p *People) ShowA() {
    fmt.Println("showA")
    p.ShowB()
}
func (p *People) ShowB() {
    fmt.Println("showB")
}

type Teacher struct {
    People
}

func (t *Teacher) ShowB() {
    fmt.Println("teacher showB")
}

func main() {
    t := Teacher{}
    t.ShowA()
}

运行结果如下:

showA
showB

Process finished with exit code 0

Go
t.ShowA()t.People.ShowA()
func main() {
    t := Teacher{}
    t.ShowA()
    fmt.Println("---------------")
    t.People.ShowA()
}

运行结果为:

showA
showB
---------------
showA
showB

Process finished with exit code 0

5. 下面代码会触发异常吗?请详细说明

package main

func main() {
    runtime.GOMAXPROCS(1)
    int_chan := make(chan int, 1)
    string_chan := make(chan string, 1)
    int_chan <- 1
    string_chan <- "hello"
    select {
        case value := <-int_chan:
            fmt.Println(value)
        case value := <-string_chan:
            panic(value)
    }
}

selctselctcasereturn

6. 下面代码输出什么?

package main

import "fmt"

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    a := 1
    b := 2
    defer calc("1", a, calc("10", a, b))
    a = 0
    defer calc("2", a, calc("20", a, b))
    b = 1
}

运行结果如下:

10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4

Process finished with exit code 0

在解题前需要明确两个概念:

deferreturnint
defer calc funcbcalc("10",a,b)10 1 2 33cal("1",1,3)
calc("20", a, b)calc("20", 0, 2)20 0 2 22cal("2",0,2)
cal("2",0,2)、cal("1",1,3)2 0 2 2、1 1 3 4

7. 请写出以下输入内容

package main

import "fmt"

func main() {
    s := make([]int, 5)
    fmt.Printf("%p\n", s)
    s = append(s, 1, 2, 3)
    fmt.Printf("%p\n", s) //new pointer
    fmt.Println(s)
}

运行结果:

0xc4200180c0
0xc42001c0a0
[0 0 0 0 0 1 2 3]

Process finished with exit code 0

8. 下面的代码有什么问题


package main

import (
    "fmt"
    "sync"
)

type UserAges struct {
    ages map[string]int
    sync.Mutex
}

func (ua *UserAges) Add(name string, age int) {
    ua.Lock()
    defer ua.Unlock()
    ua.ages[name] = age
}

func (ua *UserAges) Get(name string) int {
    if age, ok := ua.ages[name]; ok {
        return age
    }
    return -1
}

func main() {
    count := 1000
    gw := sync.WaitGroup{}
    gw.Add(count * 3)
    u := UserAges{ages: map[string]int{}}
    add := func(i int) {
        u.Add(fmt.Sprintf("user_%d", i), i)
        gw.Done()
    }

    for i := 0; i < count; i++ {
        go add(i)
        go add(i)
    }

    for i := 0; i < count; i++ {
        go func(i int) {
            defer gw.Done()
            u.Get(fmt.Sprintf("user_%d", i))
        }(i)
    }
    gw.Wait()
    fmt.Println("Done")
}

输出结果:

fatal error: concurrent map read and map write

goroutine 2022 [running]:
runtime.throw(0x10c5472, 0x21)

Getpanic
sync.Mutexmapmapfatal error: concurrent map read and map write
panicGo1.9map
sync.Mutexsync.RWMutex

RWMutex相关方法:

type RWMutex
    func (rw *RWMutex) Lock() 
    func (rw *RWMutex) RLock()
    func (rw *RWMutex) RLocker() Locker
    func (rw *RWMutex) RUnlock()
    func (rw *RWMutex) Unlock()

代码改进如下:

package main

import (
    "fmt"
    "sync"
)

type UserAges struct {
    ages map[string]int
    sync.RWMutex
}

func (ua *UserAges) Add(name string, age int) {
    ua.Lock()
    defer ua.Unlock()
    ua.ages[name] = age
}

func (ua *UserAges) Get(name string) int {
    ua.RLock()
    defer ua.RUnlock()
    if age, ok := ua.ages[name]; ok {
        return age
    }

    return -1
}

func main() {
    count := 10000
    gw := sync.WaitGroup{}
    gw.Add(count * 3)
    u := UserAges{ages: map[string]int{}}
    add := func(i int) {
        u.Add(fmt.Sprintf("user_%d", i), i)
        gw.Done()
    }
    for i := 0; i < count; i++ {
        go add(i)
        go add(i)
    }
    for i := 0; i < count; i++ {
        go func(i int) {
            defer gw.Done()
            u.Get(fmt.Sprintf("user_%d", i))
            fmt.Print(".")
        }(i)
    }
    gw.Wait()
    fmt.Println("Done")
}

运行结果如下:

.
.
.
.
Done

Process finished with exit code 0

9. 下面的迭代会有什么问题?

package main

import "fmt"
import "sync"
import "time"

type ThreadSafeSet struct {
    sync.RWMutex
    s []int
}

func (set *ThreadSafeSet) Iter() <-chan interface{} {
    ch := make(chan interface{})
    go func() {
        set.RLock()

        for elem := range set.s {
            ch <- elem
            fmt.Print("get:", elem, ",")
        }

        close(ch)
        set.RUnlock()

    }()
    return ch
}

func main() {
    //read()
    unRead()
}
func read() {
    set := ThreadSafeSet{}
    set.s = make([]int, 100)
    ch := set.Iter()
    closed := false
    for {
        select {
        case v, ok := <-ch:
            if ok {
                fmt.Print("read:", v, ",")
            } else {
                closed = true
            }
        }
        if closed {
            fmt.Print("closed")
            break
        }
    }
    fmt.Print("Done")
}

func unRead() {
    set := ThreadSafeSet{}
    set.s = make([]int, 100)
    ch := set.Iter()
    _ = ch
    time.Sleep(5 * time.Second)
    fmt.Print("Done")
}

结论:内部迭代出现阻塞。默认初始化时无缓冲区,需要等待接收者读取后才能继续写入。

chan在使用make初始化时可附带一个可选参数来设置缓冲区。默认无缓冲,题目中便初始化的是无缓冲区的chan,这样只有写入的元素直到被读取后才能继续写入,不然就一直阻塞。

设置缓冲区大小后,写入数据时可连续写入到缓冲区中,直到缓冲区被占满。从chan中接收一次便可从缓冲区中释放一次。可以理解为chan是可以设置吞吐量的处理池。

ch := make(chan interface{})ch := make(chan interface{},1)chch <- elem
11

10. 以下代码能编译过去吗?为什么?

package main
import (
    "fmt"
)
type People interface {
    Speak(string) string
}
type Stduent struct{}
func (stu *Stduent) Speak(think string) (talk string) {
    if think == "bitch" {
        talk = "You are a good boy"
    } else {
        talk = "hi"
    }
    return
}
func main() {
    var peo People = Stduent{}
    think := "bitch"
    fmt.Println(peo.Speak(think))
}

Student{}PeoplePeople

两种正确修改方法:

  • 方法一
package main
import (
    "fmt"
)
type People interface {
    Speak(string) string
}

type Stduent struct{}

func (stu Stduent) Speak(think string) (talk string) {
    if think == "bitch" {
        talk = "You are a good boy"
    } else {
        talk = "hi"
    }
    return
}
func main() {
    var peo People = Stduent{}
    think := "hi"
    fmt.Println(peo.Speak(think))
}

  • 方法二
package main
import (
    "fmt"
)
type People interface {
    Speak(string) string
}

type Stduent struct{}

func (stu Stduent) Speak(think string) (talk string) {
    if think == "bitch" {
        talk = "You are a good boy"
    } else {
        talk = "hi"
    }
    return
}
func main() {
    var peo People = &Stduent{}
    think := "bitch"
    fmt.Println(peo.Speak(think))
}

总结:指针类型的结构体对象可以同时调用结构体值类型和指针类型对应的方法。而值类型的结构体对象只能调用值类型对应的接口方法。

 

(下篇)

1. 下面代码能运行吗?为什么

type Param map[string]interface{}

type Show struct {
    Param
}

func main1() {
    s := new(Show)
    s.Param["RMB"] = 10000
}

运行结果:

panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()

Paramnilnilpanic: assignment to entry in nil map

正确的修改方案如下:

package main

import "fmt"

type Param map[string]interface{}

type Show struct {
    Param
}

func main() {

    // 创建Show结构体对象
    s := new(Show)
    // 为字典Param赋初始值
    s.Param = Param{}
    // 修改键值对
    s.Param["RMB"] = 10000
    fmt.Println(s)
}

运行结果如下:

&{map[RMB:10000]}

Process finished with exit code 0

2. 请说出下面代码存在什么问题

type student struct {
    Name string
}

func f(v interface{}) {
    switch msg := v.(type) {
        case *student, student:
            msg.Name
    }
}

有两个问题:

interface{}Nameinterface{}

3. 写出打印的结果。

type People struct {
    name string `json:"name"`
}

func main() {
    js := `{
        "name":"11"
    }`
    var p People
    err := json.Unmarshal([]byte(js), &p)
    if err != nil {
        fmt.Println("err: ", err)
        return
    }
    fmt.Println("people: ", p)
}

输出内容如下:

people:  {}

p中的属性值为空的原因是因为,name的首字母小写,修改成大写,重新运行即可。

package main

import (
    "encoding/json"
    "fmt"
)

type People struct {
    Name string `json:"name"`
}

func main() {
    js := `{
        "name":"11"
    }`
    var p People
    err := json.Unmarshal([]byte(js), &p)
    if err != nil {
        fmt.Println("err: ", err)
        return
    }
    fmt.Println("people: ", p)
}

运行结果如下:

people:  {11}

Process finished with exit code 0

4. 下面的代码是有问题的,请说明原因。

package main

import "fmt"

type People struct {
    Name string
}

func (p *People) String() string {
    return fmt.Sprintf("print: %v", p)
}

func main() {
    p := &People{}
    p.String()
}

运行结果如下:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x10c122b, 0xe)

如下所示,上面的代码出现了栈溢出,原因是因为%v格式化字符串是本身会调用String()方法,上面的栈溢出是因为无限递归所致。

5. 请找出下面代码的问题所在。

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int, 1000)
    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
    }()
    go func() {
        for {
            a, ok := <-ch
            if !ok {
                fmt.Println("close")
                return
            }
            fmt.Println("a: ", a)
        }
    }()
    close(ch)
    fmt.Println("ok")
    time.Sleep(time.Second * 100)
}

运行结果如下:

panic: send on closed channel
ok

goroutine 5 [running]:
main.main.func1(0xc420098000)

解析:出现上面错误的原因是因为提前关闭通道所致。

正确代码如下:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个缓冲通道
    ch := make(chan int, 1000)

    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
    }()

    go func() {
        for i := 0; i < 10; i++ {
            a, ok := <-ch

            if !ok {
                fmt.Println("close")
                close(ch)
                return
            }
            fmt.Println("a: ", a)
        }
    }()

    fmt.Println("ok")
    time.Sleep(time.Second)
}

运行结果如下:

ok
a:  0
a:  1
a:  2
a:  3
a:  4
a:  5
a:  6
a:  7
a:  8
a:  9

6. 请说明下面代码书写是否正确。

var value int32

func SetValue(delta int32) {
    for {
        v := value
        if atomic.CompareAndSwapInt32(&value, v(v+delta)) {
            break
        }
    }
}

atomic.CompareAndSwapInt32atomic.CompareAndSwapInt32(&value, v,v+delta)
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)*int32int32CompareAndSwapInt32·函数在被调用之后会先判断参数指向的被操作值与参数new

完整代码如下:

package main

import (
    "sync/atomic"
    "fmt"
)

var value int32

func SetValue(delta int32) {
    for {
        v := value
        // 比较并交换

        if atomic.CompareAndSwapInt32(&value, v,v+delta) {
            fmt.Println(value)
            break
        }
    }
}

func main()  {
    SetValue(100)
}

100

7. 下面的程序运行后为什么会爆异常。

package main

import (
    "fmt"
    "time"
)

type Project struct{}

func (p *Project) deferError() {
    if err := recover(); err != nil {
        fmt.Println("recover: ", err)
    }
}

func (p *Project) exec(msgchan chan interface{}) {
    for msg := range msgchan {
        m := msg.(int)
        fmt.Println("msg: ", m)
    }
}

func (p *Project) run(msgchan chan interface{}) {
    for {
        defer p.deferError()
        go p.exec(msgchan)
        time.Sleep(time.Second * 2)
    }
}

func (p *Project) Main() {
    a := make(chan interface{}, 100)
    go p.run(a)
    go func() {
        for {
            a <- "1"
            time.Sleep(time.Second)
        }
    }()
    time.Sleep(time.Second * 100)
}

func main() {
    p := new(Project)
    p.Main()
}

运行结果如下:

panic: interface conversion: interface {} is string, not int

goroutine 17 [running]:
main.(*Project).exec(0x1157c08, 0xc420068060)

stringm := msg.(int)intintstring

完整正确代码如下:

package main

import (
    "fmt"
    "time"
)

type Project struct{}

func (p *Project) deferError() {
    if err := recover(); err != nil {
        fmt.Println("recover: ", err)
    }
}

func (p *Project) exec(msgchan chan interface{}) {
    for msg := range msgchan {
        m := msg.(string)
        fmt.Println("msg: ", m)
    }
}

func (p *Project) run(msgchan chan interface{}) {
    for {
        defer p.deferError()
        go p.exec(msgchan)
        time.Sleep(time.Second * 2)
    }
}

func (p *Project) Main() {
    a := make(chan interface{}, 100)
    go p.run(a)
    go func() {
        for {
            a <- "1"
            time.Sleep(time.Second)
        }
    }()
    time.Sleep(time.Second * 100)
}

func main() {
    p := new(Project)
    p.Main()
}

运行结果如下:

msg:  1
msg:  1
msg:  1
.
.
.
msg:  1
msg:  1
msg:  1
msg:  1
msg:  1

8. 请说出下面代码哪里写错了。

func main() {
    abc := make(chan int, 1000)
    for i := 0; i < 10; i++ {
        abc <- i
    }
    go func() {
        for {
            a := <-abc
            fmt.Println("a: ", a)
        }
    }()
    close(abc)
    fmt.Println("close")
    time.Sleep(time.Second * 100)
}

go中的for循环是死循环,应该设置出口。正确代码如下:
package main

import (
    "fmt"
    "time"
)

func main() {
    abc := make(chan int, 1000)
    for i := 0; i < 10; i++ {
        abc <- i
    }
    go func() {
        for {
            a,ok := <-abc
            if !ok {
                fmt.Println("结束!")
                return
            }
            fmt.Println("a: ", a)
        }
    }()
    close(abc)
    fmt.Println("close")
    time.Sleep(time.Second * 100)
}

运行结果为:

close
a:  0
a:  1
a:  2
a:  3
a:  4
a:  5
a:  6
a:  7
a:  8
a:  9
结束!

9. 请说出下面代码,执行时为什么会报错

type Student struct {
    name string
}

func main() {
    m := map[string]Student{"people": {"liyuechun"}}
    m["people"].name = "wuyanzu"
}

value

代码作如下修改方可运行:

package main

import "fmt"

type Student struct {
    name string
}

func main() {
    m := map[string]Student{"people": {"liyuechun"}}
    fmt.Println(m)
    fmt.Println(m["people"])

    // 不能修改字典中结构体属性的值
    //m["people"].name = "wuyanzu"

    var s Student = m["people"] //深拷贝
    s.name = "xietingfeng"
    fmt.Println(m)
    fmt.Println(s)
}

运行结果如下:

map[people:{liyuechun}]
{liyuechun}
map[people:{liyuechun}]
{wuyanzu}

10. 请说出下面的代码存在什么问题

type query func(string) string

func exec(name string, vs ...query) string {
    ch := make(chan string)
    fn := func(i int) {
        ch <- vs[i](name)
    }
    for i, _ := range vs {
        go fn(i)
    }
    return <-ch
}

func main() {
    ret := exec("111", func(n string) string {
        return n + "func1"
    }, func(n string) string {
        return n + "func2"
    }, func(n string) string {
        return n + "func3"
    }, func(n string) string {
        return n + "func4"
    })
    fmt.Println(ret)
}

return <-chqueryquery