• Golang-基础自测题


    平衡二叉树: 父节点的左子树和右子树的高度之差不能大于1,也就是说不能高过1层,否则该树就失衡了,
    此时就要旋转节点,在编码时,我们可以记录当前节点的高度,比如空节点是-1,叶子节点是0,
    非叶子节点的height往根节点递增,比如在下图中我们认为树的高度为h=2。

    /* 1 写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出 */

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


    func main() {

    var count int

    ticket := time.NewTicker(time.Second)


    for{
    select {
    case <-ticket.C:
    ProtectRun(proc)
    count++
    }
    }


    //fmt.Println("t")
    }

    func ProtectRun(entry func()) {
    // 延迟处理的函数
    defer func() {
    // 发生宕机时,获取panic传递的上下文并打印
    err := recover()
    switch err.(type) {
    case runtime.Error: // 运行时错误
    fmt.Println("runtime error:", err)
    default: // 非运行时错误
    fmt.Println("error:", err)
    }
    }()

    entry()
    }

    func proc() {
    panic("ok")
    }


    /* 2.使用After 实现等待组的timeout */

    import (
    "fmt"
    "sync"
    "time"
    )

    func main() {
    wg := sync.WaitGroup{}
    c := make(chan struct{})
    for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(num int, close <-chan struct{}) {
    defer wg.Done()
    <-close
    fmt.Println(num)
    }(i, c)
    }

    if WaitTimeout(&wg, time.Second*5) { // wg.Wait() 放在循环外, 只是起到检查任务都跑完; wg.Wait() 放在循环内, 相当于加了一个锁, 只能一个任务一个任务的执行;
    close(c)
    fmt.Println("timeout exit")
    }
    time.Sleep(time.Second * 10)
    }


    func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {

    // 要求手写代码
    // 要求sync.WaitGroup支持timeout功能
    // 如果timeout到了超时时间返回true
    // 如果WaitGroup自然结束返回false


    ch := make(chan bool)

    go func(ch chan bool) {
    wg.Wait()
    ch <- false // 如果执行结束 则返回true
    }(ch)

    select {
    case done := <-ch:
    return done
    case <-time.After(timeout):
    return true
    }

    }

    3. go 造成的资源丢失的原因

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

    go 之后要使用 for{ select{} } 复用

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

    buffer := []string{}

    OuterLoop:
    for{
    select {
    case re := <-ch:
    fmt.Println("到这了?")
    fmt.Println(re)
    buffer = append(buffer,re)
    case <- time.After(time.Second*5):
    break OuterLoop
    }

    }

    return strings.Join(buffer,",")
    }

    4. 枚举类
    const (
    name = "menglu" //0
    c = iota //1
    d = iota
    )

    fmt.Println(c) //1
    fmt.Println(d) //2

    5. 下面代码写法有什么问题?

    type Stduent struct {
    Age int
    }
    func main() {
    kv := map[string]Stduent{"menglu": {Age: 21}}
    kv["menglu"].Age = 22
    s := []Stduent{{Age: 21}}
    s[0].Age = 22
    fmt.Println(kv, s)
    }


    改为:
    func main() {
    kv := map[string]*Stduent{"menglu": &Stduent{Age: 21}}
    kv["menglu"].Age = 22
    s := []Stduent{{Age: 21}}
    s[0].Age = 22
    fmt.Println(*kv["menglu"], s)
    }


    6. 补漏
    补漏

    func add(args ...int) int {}
    add([]int{1, 3, 7}...)


    func (s*Slice)Remove(value interface{}) error {

    for i, v:= range *s {

    if isEqual(value, v) {

    *s =append((*s)[:i],(*s)[i + 1:]...)

    return nil

    }

    }

    returnERR_ELEM_NT_EXIST

    }


    A. 当一个goroutine获得了Mutex后,其他goroutine就只能乖乖的等待,除非该goroutine释放这个Mutex
    B. RWMutex在读锁占用的情况下,会阻止写,但不阻止读
    C. RWMutex在写锁占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占

    A. 基本思路是将引用的外部包的源代码放在当前工程的vendor目录下面
    B. 编译go代码会优先从vendor目录先寻找依赖包
    D. 有了vendor目录后,打包当前的工程代码到其他机器的$GOPATH/src下都可以通过编译

    91. 【中级】关于slice或map操作,下面正确的是()
    A.

    92. var s []int

    s =append(s,1)

    B.

    var mmap[string]int

    m["one"]= 1

    C.

    var s[]int

    s =make([]int, 0)

    s =append(s,1)

    D.

    var mmap[string]int

    m =make(map[string]int)

    m["one"]= 1

    参考答案:ACD


    D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的


    A. select机制用来处理异步IO问题
    B. select机制最大的一条限制就是每个case语句里必须是一个IO操作
    C. golang在语言级别支持select关键字


    33. func main() {

    34. x := []string{"a", "b","c"}

    35. for v := range x {

    36. fmt.Print(v)

    37. }

    }

    参考答案:012


    panic 需要等defer 结束后才会向上传递。


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

    type student struct {
    Name string
    Age int
    }
    func pase_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 //问题在这里
    }
    }

    不能用range遍历指针, 只会得到指针指向最后一个索引的副本。

    // 正确
    for i:=0;i<len(stus);i++ {
    m[stus[i].Name] = &stus[i]
    }
    for k,v:=range m{
    println(k,"=>",v.Name)
    }


    select 中只要有一个case能return,则立刻执行。
    当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。
    如果没有一个case能return则可以执行”default”块。


    func calc(indexstring, a, bint) 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
    }


    index:1肯定是最后执行的,但是index:1的第三个参数是一个函数,所以最先被调用

    func main() {

    23. strs := []string{"one","two", "three"}

    24.

    25. for _, s := range strs {

    26. go func() {

    27. time.Sleep(1 * time.Second)

    28. fmt.Printf("%s ", s)

    29. }()

    30. }

    31. time.Sleep(3 * time.Second)

    }

    参考答案:three threethree


    遍历数组是正常的,问题在于 go func(s2 string) 匿名函数需要传参。

    type Slice []int

    62. func NewSlice() Slice {

    63. return make(Slice, 0)

    64. }

    65. func (s* Slice) Add(elem int) *Slice {

    66. *s = append(*s, elem)

    67. fmt.Print(elem)

    68. return s

    69. }

    70. func main() {

    71. s := NewSlice()

    72. defer s.Add(1).Add(2)

    73. s.Add(3)

    }

    参考答案:132

    2 作为defer第一个读取的函数,被推到栈底,然后正常执行 1 3

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

    type UserAges struct {
    ages map[string]int
    sync.Mutex
    }
    func(ua*UserAges)Add(name string, age int) {
    ua.Lock()
    deferua.Unlock()
    ua.ages[name] = age
    }
    func(ua*UserAges)Get(name string)int {
    ifage, ok := ua.ages[name]; ok {
    return age
    }
    return-1
    }


    读写要同时有锁

    func (ua *UserAges)Get(namestring)int {
    ua.Lock()
    deferua.Unlock()
    ifage, ok := ua.ages[name]; ok {
    return age
    }
    return-1
    }


    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 = "Youare a good boy"
    } else {
    talk = "hi"
    }
    return
    }
    func main() {
    var peo People = Stduent{}
    think := "bitch"
    fmt.Println(peo.Speak(think))
    }

    答案是可以,这就是接口的实现与使用。

    switch .(type){} 只能用于interface{}类型


    var i interface{}
    i = 1
    switch i.(type){
    case int:
    fmt.Println("int")
    }

    这个无法通过编译
    i := 1
    switch i.(type){
    case int:
    fmt.Println("int")
    }


    new 出来的是指针, make 出来的值


    list := new([]int)
    *list = append(*list,1)

    list2 := make([]int,0)
    list2 = append(list2,1)


    进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。


    指针为空 var a *int = nil 与 值为空 x == nil 是两码事


    常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,


    const cl = 100

    fmt.Println(cl)
    fmt.Println(&cl) 常量不分配内存地址


    type M1 int //M1 为新类型, 需要强转赋值
    type M2 = int //M2 为别名

    var i int = 9

    var i1 M1 = M1(i)
    var i2 M2 = i

    fmt.Println(i1,i2)


    func test() []func() {
    var funs []func()
    for i := 0; i < 2; i++ {
    funs = append(funs, func() { // 匿名函数就是闭包, 具有延迟执行与静态保存的特性, 要么直接往匿名函数中传参, 要么在外层定义变量保存值;
    println(&i, i)
    })
    }
    return funs
    }


    funs := test()
    for _, f :=range funs{
    f()
    }


    func main() {

    funs := test()
    for _, f :=range funs{
    f()
    }
    }

    x := i

    funs = append(funs, func() {
    println(&x, x)
    })

    ###################################################################
    grpc:

    //向grpc服务发送信号, 检测是否连接;
    signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
    for {
    s := <-c
    log.Info("get a signal %s", s.String())
    switch s {
    case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
    svc.Close()
    log.Info("zeus-service exit")
    time.Sleep(time.Second)
    return
    case syscall.SIGHUP:
    default:
    return
    }
    }

    http service 之间通过 header 形式进行传递,rpc service 之间通过 rpc metadata 形式进行传递。

    1. 先初始化 serve 数据库连接池等
    ... 创建内部rpc服务,
    net/rpc/warden
    func New(c *conf.Config, s *v1srv.ZeusService) *warden.Server {
    ws := warden.NewServer(nil)
    v1pb.RegisterZeusServer(ws.Server(), s)
    ws, err := ws.Start()
    if err != nil {
    panic(err)
    }
    return ws
    }

    如果我们用socket实现RPC,那么就能获取性能上的优势。在大并发的情况下,RPC的性能优于RESTful
    所以通常情况下,我们对内是用rpc提供服务,对外用RESTful提供服务

    golang官方给我们提供了一个net/rpc的库使用,它支持tpc,http,JSONRPC数据传输。但是它只能用于go内部使用,为什么?
    因为它使用了一个go自带的库(encoding/gob)来编码和解码。如果需要跨平台那么需要其它的rpc框架了,比如thrift,gRPC等等


    net/rpc的远程调用还有一些附加条件:

    1.函数必须是导出的(首字母大写)
    2.必须有两个导出类型的参数
    3.第一个参数是接收的参数,第二个参数是返回给客- 户端的参数,第二个参数必须是指针类型的
    4.函数还要有一个返回值error


    type Arith int

    func (t *Arith) Multiply(args *string, reply *int) error {
    return nil
    }

    arith := new(Arith)
    rpc.Register(arith)
    rpc.HandleHTTP()
    l, e := net.Listen("tcp", ":1212")
    if e != nil {
    log.Fatal("listen error: ", e)
    }
    http.Serve(l, nil)

    简单分析一下上面的例子,先实例化了一个Arith对象arith,然后给arith注册了rpc服务,然后把rpc挂载到http服务上面,
    当http服务打开的时候我们就可以通过rpc客户端来调用arith中符合rpc标准的的方法了

    // 用到3个方法, rpc, net, http


    //客户端
    client, err := rpc.DialHTTP("tcp", "127.0.0.1:1212")
    client.Call("Arith.Multiply", "call", &reply)

    如何在go routing中捕获异常, 并恢复:
    一种方法是抽象出方法,
    ProtectedRun(f func()){
    defer func(){
    err := recover()
    }
    f()
    }

    go ProtectRun()


    另一种方法是, 使用反射切入

    func newF(){

    go func(){
    defer func(){
    err := recover()

    ff := reflect.ValueOf(f)
    ff.Call()
    }
    }

    }


    #############################################################################################
    嵌套服务goroutine

    当一个服务包含多个子服务时, 每个子服务又都有goroutine, 那如何只关闭这个子服务的goroutine?

    func main(){

    ctx, cancel := context.WithCancel(context.Backgroud())
    go doStuff(ctx)

    time.Sleep(10*time.Seconds)
    cancel() //关闭该子服务, 将关闭信息传入context通道;

    }

    func doStuff(ctx context.Backgroud){
    for{
    select{
    case <-ctx.Done():
    return
    default:
    fmt.Print("working")
    }
    }
    }

    // 两种单例模式


    import "sync"
    import "sync/atomic"

    var initialized uint32
    ...

    func GetInstance() *singleton {

    if atomic.LoadUInt32(&initialized) == 1 {
    return instance
    }

    mu.Lock()
    defer mu.Unlock()

    if initialized == 0 {
    instance = &singleton{}
    atomic.StoreUint32(&initialized, 1)
    }

    return instance
    }

    import (
    "sync"
    )

    type singleton struct {
    }

    var instance *singleton
    var once sync.Once

    func GetInstance() *singleton {
    once.Do(func() {
    instance = &singleton{}
    })
    return instance
    }

  • 相关阅读:
    麦肯锡 问题分析与解决技巧
    JavaSe_IO流总结
    tcpdump
    interface
    phpexcel 合并单元格后的动态行高
    分布式事务锁的实现
    IntelliJ IDEA 实用快捷键
    Redis安装教程
    ng new my-app创建angular项目是卡住的解决办法
    SqlYog无法连接mysql数据库(包括docker环境)的解决方法
  • 原文地址:https://www.cnblogs.com/ruili07/p/11458851.html
Copyright © 2020-2023  润新知