zoukankan      html  css  js  c++  java
  • 关于go的疑问

    这必然是一路风景

    1、go的时间time.sleep()其中的参数默认单位不是秒而是微秒 go中有专门的时间值time.Second/Minute/Hour...

    2、channel的 make(chan int, arg)第二个参数定义channel buffer,默认是unbuffer,单线程使用的时候必须要定义,否则会被阻塞(unbuffer意思是只要我收到了,我就会阻塞在这个地方,等人来取,单线程就会直接报错了编不过,例子如下)https://www.jianshu.com/p/f12e1766c19f

    func Ping(ping chan<- int, s int) {
    	ping <- s
    }
    
    func Pong(ping <-chan int, pong chan<- int) {
    	msg := <-ping
    	pong <- msg
    }
    
    func ChannelDirect() {
    	pi := make(chan int)          // 会报错当ping <- s的时候
    	po := make(chan int)
    	Ping(pi, 1)
    	Pong(pi, po)
    	fmt.Println(<-po)
    }
    

    3、fmt.printf("%T", arg) 判断arg的类型

    4、channel synchronization 其实就是通过在goroutine里面放置channel的方式,保证goroutine中的代码运行完毕

    func worker(done chan bool) {
        fmt.Print("working...")
        time.Sleep(time.Second)
        fmt.Println("done")
    
        done <- true
    }
    
    func main() {
    
        done := make(chan bool, 1)
        go worker(done)
        <-done                           // 这里阻塞直到goroutine运行完
    }
    

    5、goroutines leaking 永远不要在不知道它将如何停止的情况下启动一个goroutine,因为是并发的,所以会一直占用内存,其中一种情况就是channel没有设置buffer,则默认为阻塞的,如果channel阻塞且正好在goroutines中,则会造成此goroutine永远释放不了

    6、数组和切片的区别:1.定义的时候数组必须定长度或者[...]让机器判定长度,切片则不用。多维数组只有第一层能使用[...]

    7、数组传递的是值,是值类型,map和slice传递的是指针

    8、切片是对底层数组的进一层封装,所以切片是由数组切来的时候,并不是复制了一份,而是直接的引用,改变数组的值会同时更改切片的值,切片的赋值也有相同效果,赋值的结果是复制了引用,底层数组是同一个

    9、当切片append元素时,如果append后长度未超出cap长度,则依然适用上面的规则,如果append后超出了,则会复制一个新的底层数组重新指向,此时再更改上面的数组就不再影响slice

    func ArraySlice() {
    	new_array := [3]int{1, 3, 5}
    	new_slice := new_array[:2]
    	fmt.Println(new_slice)
    	new_slice = append(new_slice, 9, 10)
    	new_array[1] = 7
    	fmt.Println(new_slice)
    	fmt.Println(cap(new_slice))
    }
    stdout-->
    [1 3]
    [1 3 9 10]
    6  // 原来是3
    

    9、切片没有删除方法,可以append(slice[:index], slice[index+1:]...),删除掉index位置的元素就是从index+1开始append到index位置其实,注意第二个后面的三个点是go的语法糖,必须要加的,不然会直接append一个切片上去,其实就类似python的*args的*

    10、切片是否为空必须要用len是否为0来判断,不能弄nil

    11、new与make的区别:

    1. 二者都是用来做内存分配的。

    2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;

    3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针

    4. new和&在用于结构体时没什么区别,

      a := &simple_struct{}
      a := new(simple_struct)
      输出的都是*simple_struct
      

      但是对于基本类型有区别,例如不可能出现这种语法 a := &int() 只能是a:=new(int)

    12、map是没有cap的,所以make(map, 111)第二个参数是没用的(目前暂时认为,不确定),map也是一个指向底层数据的指针,是引用类型

    13、go的goroutine并发不是越多越好,因为协程是go的内部处理调度,绑定的是固定几个cpu的内核线程,所以当goroutine到达一定数量,cpu被一直满载占用,此时再创建协程运算效率就并不会有什么明显提升了,而go中协程的调度负荷反而会越来越重,有可能还会造成性能的下降(个人理解)

    14、for index, stu := range stus {},这个时候index和stu是固定的内存空间,range的作用把stus解析后赋值给前面两个变量,前面两个变量的地址是不变的,所以在这里面尽量不要有关于前面两个变量的指针操作

    15、go语言指针的使用需要声明后用new分配内存空间,struct的属性占用连续的内存空间,排列方法是属性内存和结构体内存的对齐,可以从这个方面优化大体积结构体

    16、go语言的方法接收者用指针比较常见,struct是值类型,如果接收者是值类型则修改只在局部函数中生效,方法可以对任意本地类型生效,例如type MyInt int,那么给MyInt加一个方法完全可以

    什么时候应该使用指针类型接收者
    需要修改接收者中的值
    接收者是拷贝代价比较大的大对象
    保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。
    

    补充:接收者如果是值类型,那么当对象是指针类型是可以直接使用的,因为go加了语法糖自动*obj取值了,反之接收者如果是指针类型,不能使用值类型对象。

    17、go的继承通过结构体的嵌套实现,被嵌套的结构体是父类,结构体变量大写开头为公开,小写为私有(一定要注意,json.Marshal就不会读取私有的,如果是tag 小写是可以的例如`json:"name"`)

    18、defer是延迟语句,执行在函数return值的时候(没有return其实就是默认return nil猜测,因为defer是可以正常执行的,但是我用goland无法获取无返回值的值会直接报错), defer延迟了语句执行,但是没有延迟传参操作,如果defer之后更改了defer函数的入参值,defer时更改是不会生效的,因为当传参时是进行了copy操作,{补充:函数传参是值拷贝形式}

    func Test(A int ) {
    d := &A
    fmt.Printf("%p
    ", &A)
    defer func(a int) {
    	fmt.Printf("%p
    ", &a)
    	fmt.Println(a)
    }(A)
    *d = 12
    fmt.Println(A)
    fmt.Println("in Test")
    }
    
    0xc00000a200
    12
    in Test
    0xc00000a230  // 和上面指针已经不一样了,所以是复制了一份
    11
    

    19、go tool compile -S XXX.go 可以把go编译到汇编不生成binary

    20、go语言错误处理机制panic相当于python的raise,recover相当于try/except,不过recover必须搭配defer使用并且位置在panic之前,因为panic是直接抛出异常如果在后面就直接报错了无法捕获(写到最开头不就完了)

    21、interface{}虽然好用,最好少用

    22、函数返回值中所用名字,此函数中不能再次初始化定义

    func test() A string {
        A = "hello"
    }
    此时不能用 A:= 
    

    23、判定类型时可以使用a.(类型),同时也是类型的强转,返回两个值,第一个值是转化后数据,第二个值是类型判定true还是false,或者用switch可以写成a.(type),只输出true或false,之后用case判定输出类型

    24、go语言写入文件语句

    w1, err = os.OpenFile(".\logtest.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644),最后一个是文件的permission,类比linux  777 755,第二个参数可以点进去看源码,对应写文件模式
    	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
    	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
    	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
    	// The remaining values may be or'ed in to control behavior.
    	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
    	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
    	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
    	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
    	O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
    
    而w1.WriteString(msg.(string))输出两个值,第一个值是写入文件的字节数,第二个是err
    

    25、如果方法带参数,接口也必须带参数

    26、reflect.ValueOf(x).Elem()返回x指针值,作用是什么?这个reflect要单独开一章讲

    27、unicode.IsLetter、unicode.IsDigit、unicode.IsSpace判断字符类型,传参int32一般需要转化一下

    28、strings.ToLower strings.ToUpper转化大小写

    29、go数组是值类型,可以通过赋值来copy,而切片是引用类型的,需要用copy方法,一维切片可以用浅copy,多维切片需要深copy,因为内部切片也是引用,浅copy复制的是引用不能完全复制。具体方法参照此连接

    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 有任何问题请随时交流~ Email: araise1@163.com
  • 相关阅读:
    Mac电脑上的软件以及作用整理
    sed命令在mac和linux下的区别
    gin框架开启web跨域
    Mac下输入特殊字符的总结
    4月4悼念日 多数app 或者网站变成灰色的实现效果
    Mqtt 客户端多主题订阅
    paho.mqtt.golang--option.go 源码解析
    MQTT服务器(Broker)
    asdine/storm学习笔记
    vuex基本使用教程
  • 原文地址:https://www.cnblogs.com/seasen/p/12981788.html
Copyright © 2011-2022 走看看