什么是go语言
go是一门并发支持,垃圾回收的编译型 系统编程语言,旨在创造一门具有静态编译语言的高性能和动态语言的高效开发之间拥有一个良好平衡点 的一门编程语言。
go有什么优点?
- 自动垃圾回收机制,和python类似,开发者不需要关心内存垃圾是怎么回收的,由系统自动判断在合适的时候(比如CPU相对空闲的时候)进行自动垃圾收集工作
- 更丰富的内置类型,更丰富的内置类型,除了常规的数组字符串之外,还有字典类型(map),数组切片(slice)等等
- 函数多返回值,和python一样,可以在return 的时候返回很多个值。
- 错误处理,go语言中引入了三个关键字用于标准的错误处理流程,分别是defer、panic和recover。
- 匿名函数和闭包,匿名函数就是没有命名的函数
- 类型和接口
- 并发编程,go语言中引入了goroutine概念,它使得并发编程非常简单,使用消息传递来共享消息而不是使用共享内存来通信。
- 反射,和python里面的反射都差不多
go的安装
安装的话我们可以看官网,我这里在windows下开发,所以使用的msi版本的
官网下载地址
安装完成之后,我们看看环境变量的作用。
C:UsersLeo>go env
set GOARCH=amd64 # CPU类型
set GOBIN= # 工作目录下的bin文件夹
set GOEXE=.exe # 生成可执行文件的后缀
set GOHOSTARCH=amd64 #
set GOHOSTOS=windows # 使用到交叉编译的时候才会使用
set GOOS=windows # 当前系统版本
set GOPATH=C:UsersLeogo # 工作目录
set GORACE=
set GOROOT=C:Go # 安装目录
set GOTOOLDIR=C:Gopkg oolwindows_amd64 # 工具目录
set GCCGO=gccgo
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0
set CXX=g++
set CGO_ENABLED=1
set PKG_CONFIG=pkg-config
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
根据约定,我们需要在GOPATH下需要创建三个目录
- pkg 存放编译后生成的包文件
- bin 存放编译后生成的可执行文件
- src 存放项目的源码
那么问题来了,我们在windows下怎么去配置GOPATH呢?
我们 在windows上添加一个系统变量为GOPATH即可。目录自己制定,注意变量名一定是要大写的GOPATH。 我的GOPATH设置为:H:Goproject
go 常用命令介绍
- go get 获取远程包(需要提前安装git或者hg)
- go run 直接运行程序
- go build 测试编译,检查是否有编译错误
- go fmt 格式化源码(部分IDC在保存的时候自动调用)
- go install 编译包文件并编译整个郑旭
- go test 运行测试文件
- go doc 查看文档
例如:C:UsersLeo>godoc fmt Println
有时候我们无法访问官网,那么怎么在本地搭建一个官网来查看模块的用法呢?通过下面这条命令就能轻松搭建好:
C:UsersLeo>godoc --http=:8080
然后我们访问127.0.0.1:8080就可以了
IDE的选择
IDC多种多样,我这里选择一个原生支持go的IDC,叫做liteide,等同于pycharm一样原生支持python,不需要安装其他插件,
liteide下载地址
下载后解压即可使用,国人写的IDE,原生中文。
我们新建一个go文件,文件名是hello.go,我的这个hello.go文件是在GOPATH下面的src下面,
hello world
开始写一个Hello world,我们先不要去纠结每个干嘛的,我就先简单说下:
// hello 注释
package main // package名字为main表示编译后为可执行文件,否则的话是后缀为.a的包(模块)
import ( // 导入一个包
"fmt"
)
func main() { // 主函数,print hello,既没有参数也没有返回值,执行main函数必须在package main里面
fmt.Println("Hello World!晚安,Leo先生")
}
解释下:
- package: 每个go源码文件的开头都是一个package声明,表示该go代码所属的包。包是go语言里最基本的分发单位,也是工程管理中依赖关系的体现。
- main : 要生成可执行程序,必须建立一个名字为main的包,并且在该包中包含一个叫main()的函数(该函数是Go可执行程序的执行起点),main函数不能带参数,也不能定义返回值。
下面执行这个文件:
H:GoDevelopmentsrc>go run hello.go
Hello World!晚安,Leo先生
如果使用IDE运行的话,有两种方式:
- 找到右上角的build 按钮编译下,然后在点击run按钮才能够执行,此时你发现这个src目录下多了 一个src.exe可执行文件了。
- 点击IDE右上角的fileRun即可运行
以下代码可以查看我的github
包的简单代码实现:
我们下面看看几个简单的包,来充分了解下Go下面的包是怎么样的。
我的目录如下:
我这次要说的就是package_example下的代码。 package_example代码是用来做加法和减法的代码。 package_example下面的有两个子目录,一个calc子目录是负责计算的包,main是main包。
我们首先看看calc下面的add.go和sub.go:
add.go
// add
package calc // 包名要和这个目录一致,表示这个go文件属于这个包的。
func Add(a, b int) int { // 定义的函数,看不懂先别管。
return a + b
}
sub.go
// sub
package calc
func Sub(a, b int) int {
return a + b
}
- 我们要注意查看下面两段代码的package关键字,你们可以看到它们都是些的calc,为啥,因为他们所在目录就是calc,他们所实现的功能都是calc包的功能,所以必须写成calc,这样add.go和sub.go才是属于calc包的。
- calc下面的代码文件的方法如果想要被外部函数调用,那么函数名首字母必须要大写才可以。
再看下main包里面的main.go
main.go
// main
package main
import (
"fmt"
"go_dev/day1/package_example/calc"
)
func main() {
ss := calc.Add(100, 200)
su := calc.Sub(200, 123)
fmt.Println("sum=", ss)
fmt.Println("su=", su)
}
- 在main.go里面,我们可以看到package是main,因为这个是主程序的入口,在这个main目录下可以有多个文件的package写成main,但是只有一个文件可以有main函数。
- 我们要在main文件里导入calc的包,那么前提条件你是设值好了GOPATH路径,我的GOPATH路径是H:Goproject,所以我导入calc的包的时候,就以 src下的go_dev开始,到calc目录为止即可。
那么写完代码编译的话,我们就可以编译了,编译的话我们只需要对main包下面的编译就行,因为只有main包下面的编译后才是可执行文件。编译前提的是你的GOPATH是设了值,比如我这里的GOPATH是H:Goproject,那么我们编译的时候就不需要写src了,直接下src下面的路径即可,一直到main这个子目录下,不要写到main.go这个代码文件上。了解以后可以这么编译:
H:Goproject> go build go_devday1package_examplemain
也可以添加-o参数,指定exe文件的存放位置:
H:Goproject>go build -o bin/hehe.exe go_devday1package_examplemain
编译完成后就可以执行了:
H:Goprojectin>hehe.exe 指定放在bin下面的那么就在bin下面执行hehe.exe即可
H:Goproject>main.exe 没有指定那么就直接执行main.exe
提前尝鲜 goroutine
我们知道Go天生并发,所以我们可以提前看看goroutine的并发功能,同样我们还是用了上面我们所说的包组织,让大家更加了解包组织结构。
包组织目录如下:
我们先聊下进程间通信的方式,进程间通信的方式是采用管道/消息队列 来交换数据的,所以我们使用goroutine也一样,使用channel来让goroutine与main函数交换数据的。
还是先看看calc下面代码:
add.go
// add
package calc
func Add(a, b int, p chan int) {
p <- (a + b) // 把和放入到channel里面,让main函数从中取值。
}
sub.go
// sub
package calc
func Sub(a, b int, p chan int) {
p <- (a - b) // 把差放入到channel里面,让main函数从中取值。
}
我们在看看main目录下面的main.go
main.go
// main
package main
import (
"fmt"
"go_dev/day1/goroutine_example/calc"
)
var p chan int // 声明p是一个只可以放入int型的channel
func main() {
p = make(chan int, 3) // 初始化这个channel,缓冲区为3,
go calc.Add(10, 324, p) // 使用go这个关键字来启动goroutine
go calc.Sub(123123, 123, p)
ss := <-p // 从channel里面获取一个数据
su := <-p
fmt.Println("sum=", ss) //打印这个数据
fmt.Println("sub=", su)
}
写完后就可以编译执行了,执行参考步骤上面所说的。
上面的几段代码就简单演示了goroutine的使用方法。后期我会细写goroutine的概念与方法。反正我是深深感受了go天生并发的好处,personally,其他语言的多进程/线程并发编程我感觉就是噩梦一场,挺费劲的。