Go语言初探

China☆狼群 提交于 2020-02-02 07:42:11

Go语言基础

规则

大写字母开头变量为共有变量,小写字母开头为私有变量(函数类似)

Iota

iota枚举(默认开始值为0,const中每增加一行,值加1)

slice

引用类型(类似于动态数组),相比于array,它没有长度限制

make & new

  • make返回初始化后的值(非零),只能被用于slice、map、channel
  • new返回指针,一般很少被使用

goto(无条件跳转,配合标签使用,无条件跳转至标签,可用于跳出循环)

由于可随意跳转,在庞大的系统中不利于debug,所以在coding中会通过结构化程序设计来规避这类问题,那么,在go语言设计者为何会继续沿用goto呢?

  • 最开始提出goto有害论主要是当时的背景导致的,最开始使用的是非结构化的过程语言,例如BASIC和各种汇编语言
  • 可读性强,可以书写出干净的退出功能(将所有退出方式统一编辑,利用goto进行跳转)
  • 低级性能改进,goto语句执行的非常快

defer(延迟语句,函数执行到最后会按照逆序执行defer语句,可用于处理资源泄漏问题)

  • 在函数中使用时,采用先进后出模式

panic & recover(go没有异常机制,使用这两个内置函数来代替异常机制)

  • 调用panic中断函数执行(但defer语句仍然会被执行),退出执行
  • 利用recover + defer可以捕获panic的输入值,并恢复正常执行 
package main

import "fmt"

func test_goto() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()
	for sum := 0; sum < 15; sum++ {
		if sum == 12 {
			panic("12 is error")
		}
		fmt.Println(sum)
	}
}

func main() {
	test_goto()
	fmt.Println("over!")
}


输出结果为:
0
1
2
3
4
5
6
7
8
9
10
11
12 is error
over!

使用指针作为receiver

如果不使用指针作为receiver,那么传递的是参数的copy,如果修改值,只会作用在copy上

interface

GoLang本身是不支持泛型(通用类型)编程的,这也是它被诟病最多的地方,为了弥补这一缺陷,可以通过interface来实现泛型编程(PS:go只是暂时不支持,因为这会增加设计的复杂度,以后可能会支持)

反射

Go利用reflect实现反射(即检查程序在运行时的状态),要去反射是一个类型的值 (这些值都实现了空 interface),首先需要把它转化成 reflect 对象 (reflect.Type 或者 reflect.Value,根据不同的情况调用不同的函数)。

要反射的字段必须是可读写的字段,否则会报错。

并发

goroutine

goroutine 是 Go 并行设计的核心。goroutine 说到底其实就是协程,但是它比线程更小,十几个 goroutine 可能体现在底层就是五六个线程,Go 语言内部帮你实现了这些 goroutine 之间的内存共享。执行 goroutine 只需极少的栈内存 (大概是 4~5 KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine 比 thread 更易用、更高效、更轻便。

设计上我们要遵循:不要通过共享来通信,而要通过通信来共享。

但在 Go 1.5 以前调度器仅使用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在我们的程序中显式调用 runtime.GOMAXPROCS (n) 告诉调度器同时使用多个线程。GOMAXPROCS 设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。如果 n < 1,不会改变当前设置。

channel

channel类型的值可以发送接收值,注意,必须使用 make 创建 channel。默认情况下,channel 接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得 Goroutines 同步变的更加的简单,而不需要显式的 lock。所谓阻塞,也就是如果读取(value := <-ch)它将会被阻塞,直到有数据接收。其次,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲 channel 是在多个 goroutine 之间同步很棒的工具。

 Go 也允许指定 channel 的缓冲大小,很简单,就是 channel 可以存储多少元素。ch:= make (chan bool, 4),创建了可以存储 4 个元素的 bool 型 channel。在这个 channel 中,前 4 个元素可以无阻塞的写入。当写入第 5 个元素时,代码将会阻塞,直到其他 goroutine 从 channel 中读取一些元素,腾出空间。

select

select可以监听channel上的数据流动。select默认是阻塞的,只有当监听的 channel 中有发送或接收可以进行时才会运行,当多个 channel 都准备好的时候,select 是随机的选择一个执行的。

select {
case i := <-c:
    // use i
default:
    // 当 c 阻塞的时候执行这里
}

select其实就是类似 switch 的功能,default 就是当监听的 channel 都没有准备好的时候,默认执行的(select 不再阻塞等待 channel)。

我们可以使用select来设置超时以避免goroutine阻塞导致的整个程序阻塞。

func main() {
    c := make(chan int)
    o := make(chan bool)
    go func() {
        for {
            select {
                case v := <- c:
                    println(v)
                case <- time.After(5 * time.Second):
                    println("timeout")
                    o <- true
                    break
            }
        }
    }()
    <- o
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!