2018年,初识Go,用它写过一个区块链项目,不过也仅仅止步于此,后续工做都是Java和Scala居多,不过Go发展算是较晚的语言,以其简洁,快等特色很快被大众所熟知,一直认为它是一门很是优秀的语言,后续的工做场景也使用较多Go,重拾起来也不算太难。从值不值得深刻学习Go方面讲,其如今在高并发,云场景,游戏服务端等愈来愈被各大厂所使用,好比字节,B站,脉脉等,另外Go后面也有老爸-Google撑着,因此知名度愈来愈高,语言的最终目的不就是为了简洁吗,而Go作到了这一点,从我的角度讲,Go还没Java那么卷,因此竞争力越早越有优点。html
为何是Go - Go的特色
Go提供了几种基本但非必需的类型,好比切片,接口和通道。程序员
Go简单不是它的主要卖点,作为一门静态语言,Go却和不少动态脚本语言同样得灵活是Go的主要卖点,节省内存、程序启动快和代码执行速度快合在一起是Go的另外一个主要卖点,Go是一门编译型的和静态的编程语言。Go诞生于谷歌研究院web
-
内置并发编程支持:算法
-
使用协程(goroutine)作为基本的计算单元。轻松地建立协程。编程
-
使用通道(channel)来实现协程间的同步和通讯。安全
-
内置了映射(map)和切片(slice)类型。微信
-
支持多态(polymorphism)。网络
-
使用接口(interface)来实现裝盒(value boxing)和反射(reflection)。闭包
-
支持指针。架构
-
支持函数闭包(closure)。
-
支持方法。
-
支持延迟函数调用(defer)。
-
支持类型内嵌(type embedding)。
-
支持类型推断(type deduction or type inference)。
-
内存安全。
-
自动垃圾回收。
-
良好的代码跨平台性。
编译时间的长短是开发愉悦度的一个重要因素。编译时间短是不少程序员喜欢Go的一个缘由
运行第一个go程序
## 运行go程序
go run
## 打包go程序,生成可执行文件
go build
## 来安装一个第三方Go程序的最新版本,(至GOBIIN目录) 在Go官方工具链1.16版本以前,对应的命令是go get -u example.com/program(如今已经被废弃而再也不推荐被使用了
go install
## 检查可能的代码逻辑错误
go vet
## 获取网络的依赖包,用拉添加、升级、降级或者删除单个依赖,不如go mod tidy经常使用?
go get -u
## 生成go.mod 文件,依赖到的模块
go mod init Demo.go
## 扫描当前项目中的全部代码来添加未被记录的依赖至go.mod文件或从go.mod文件中删除再也不被使用的依赖
go mod tidy
## 格式化源文件代码
go fmt Demo.go
## 运行单元和基准测试用例
go test
## 查看Go代码库包的文档
go doc
## 运行go help aSubCommand来查看一个子命令aSubCommand的帮助信息
go help
GOROOT和GOPATH
GOROOT是Go语言环境的安装路径,在安装开发环境时已经肯定 GOPATH是当前项目工程的开发路径,GOPATH能够有多个,每一个GOPATH下的通常有三个包,pkg、src和bin,src用于存放项目工程的源代码文件,pkg文件夹下的文件在编译时自动生成,bin目录下生成*.exe的可执行文件。PS:每个GOPATH下均可以有pkg、src、bin三个文件夹,当设置多个GOPATH时,当前GOPATH的src源文件编译结果和生成的可执行文件会存储在最近路径的GOPATH的pkg和bin文件夹下,即当前GOPATH下,开发时在src目录下新建目录并创建源代码文件,目录名称和源文件名称能够不一样,源文件内第一行代码package pkgName中的pkgName也能够和源文件所在文件夹名称不一样。可是,若是此包须要在其余包中使用,编译器会报错,建议package 后的名称和文件所在文件夹的名称相同。通常只有main函数所在的源文件下才会出现所在包和“package 包名”声明的包名不一样的状况
最终import 的包须要时package中写的而不是目录名,不然会报错
Go的25个关键字
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
-
const
、func
、import
、package
、type
和var
用来声明各类代码元素。 -
chan
、interface
、map
和struct
用作 一些组合类型的字面表示中。 -
break
、case
、continue
、default
、else
、fallthrough
、for
、goto
、if
、range
、return
、select
和switch
用在流程控制语句中。详见基本流程控制语法。 -
defer
和go
也能够看做是流程控制关键字, 但它们有一些特殊的做用。详见协程和延迟函数调用。
ps: uintptr、int以及uint类型的值的尺寸依赖于具体编译器实现。一般地,在64位的架构上,int和uint类型的值是64位的;在32位的架构上,它们是32位的。编译器必须保证uintptr类型的值的尺寸可以存下任意一个内存地址
常量自动补全和iota
在一个包含多个常量描述的常量声明中,除了第一个常量描述,其它后续的常量描述均可以只有标识符部分。Go编译器将经过照抄前面最紧挨的一个完整的常量描述来自动补全不完整的常量描述。好比,在编译阶段,编译器会将下面的代码
const (
X float32 = 3.14
Y // 这里必须只有一个标识符
Z // 这里必须只有一个标识符
A, B = "Go", "language"
C, _
// 上一行中的空标识符是必需的(若是
// 上一行是一个不完整的常量描述)。
)
自动补全为
const (
X float32 = 3.14
Y float32 = 3.14
Z float32 = 3.14
A, B = "Go", "language"
C, _ = "Go", "language"
)
iota
是Go中预声明(内置)的一个特殊的有名常量。iota
被预声明为0
,可是它的值在编译阶段并不是恒定。当此预声明的iota
出如今一个常量声明中的时候,它的值在第n个常量描述中的值为n
(从0开始)。因此iota
只对含有多个常量描述的常量声明有意义。
iota
和常量描述自动补全相结合有的时候可以给Go编程带来很大便利。好比,下面是一个使用了这两个特性的例子
const (
Failed = iota - 1 // == -1
Unknown // == 0
Succeeded // == 1
)
const (
Readable = 1 << iota // == 1
Writable // == 2
Executable // == 4
)
能够看出,iota能够在状态值常量的应用上很方便,可是注意是在编译时候使用哦
变量声明方式
go语言中主要有下面两种声明方式
标准变量声明方式
var (
lang, bornYear, compiled = "Go", 2007, true
announceAt, releaseAt int = 2009, 2012
createdBy, website string
)
注意,Go声明的局部变量要被有效使用一次,包变量没有限制,另外不支持其余语言所支持的连等赋值,以下图:
var a, b int
a = b = 123 // 语法错误
短变量声明方式
package main
func main() {
// 变量lang和year都为新声明的变量。
lang, year := "Go language", 2007
// 这里,只有变量createdBy是新声明的变量。
// 变量year已经在上面声明过了,因此这里仅仅
// 改变了它的值,或者说它被从新声明了。
year, createdBy := 2009, "Google Research"
// 这是一个纯赋值语句。
lang, year = "Go", 2012
print(lang, "由", createdBy, "发明")
print("并发布于", year, "年。")
println()
}
:=
这种方式声明的还有个特色就是,短声明语句中必须至少有一个新声明的变量,能够支持前面部分声明过的变量再继续声明赋值
函数声明和调用
分别为 func
函数名 参数 返回值 函数体(包含返回值或者不包含)
其余都好理解,有一点须要注意在Go中和其余语言不一样之处,就是返回值若是匿名声明,那么return 的时候须要明确返回变量或者某个值,若是是非匿名声明,那么就能够只写return
这个和其余语言仍是有一点区别的,若是函数没有返回值 ,那么return
就不用写,Go不支持输入参数默认值。每一个返回结果的默认值是它的类型的零值。好比,下面的函数在被调用时将打印出(和返回)0 false
func f() (x int, y bool) {
println(x, y) // 0 false
return
}
// 我的不是很懂这个,理解不了
Go流程控制语法
三种基本的流程控制代码块:
-
if-else
条件分支代码块; -
for
循环代码块; -
switch-case
多条件分支代码块。
还有几种和特定种类的类型相关的流程控制代码块:
-
容器类型相关的
for-range
循环代码块。 -
接口类型相关的
type-switch
多条件分支代码块。 -
通道类型相关的
select-case
多分支代码块。
Go并发同步 - sync.WaitGroup
并发编程的一大任务就是要调度不一样计算,控制它们对资源的访问时段,以使数据竞争的状况不会发生。此任务常称为并发同步(或者数据同步)。Go支持几种并发同步技术,先学习最简单的一种,sync
标准库中的WaitGroup
来同步主协程和建立的协程
WaitGroup
类型有三个方法(特殊的函数,将在之后的文章中详解):Add
、Done
和Wait
。此类型将在后面的某篇文章中详细解释,目前咱们能够简单地认为:
-
Add
方法用来注册新的须要完成的任务数。 -
Done
方法用来通知某个任务已经完成了。 -
一个
Wait
方法调用将阻塞(等待)到全部任务都已经完成以后才继续执行其后的语句
package main
import (
"log"
"math/rand"
"time"
"sync"
)
var wg sync.WaitGroup
func SayGreetings(greeting string, times int) {
for i := 0; i < times; i++ {
log.Println(greeting)
d := time.Second * time.Duration(rand.Intn(5)) / 2
time.Sleep(d)
}
wg.Done() // 通知当前任务已经完成。
}
func main() {
rand.Seed(time.Now().UnixNano())
log.SetFlags(0)
wg.Add(2) // 注册两个新任务。
go SayGreetings("hi!", 10)
go SayGreetings("hello!", 10)
wg.Wait() // 阻塞在这里,直到全部任务都已完成。
}
能够看到一个活动中的协程能够处于两个状态:运行状态和阻塞状态。一个协程能够在这两个状态之间切换
协程的调度
编译器采纳了一种被称为M-P-G模型的算法来实现协程调度。其中,M表示系统线程,P表示逻辑处理器(并不是上述的逻辑CPU),G表示协程。大多数的调度工做是经过逻辑处理器(P)来完成的。逻辑处理器像一个监工同样经过将不一样的处于运行状态协程(G)交给不一样的系统线程(M)来执行。一个协程在同一时刻只能在一个系统线程中执行。一个执行中的协程运行片刻后将自发地脱离让出一个系统线程,从而使得其它处于等待子状态的协程获得执行机会,以下图,P能够理解为总司令,G是程序写好的逻辑协程,而M是具体的系统线程执行器
总结
有过语言基础的小伙伴入门Go不算很难 ,一周上手项目,一个月学会常见轮子,半年能够从0-1手写项目,一年能够在简历上写熟悉Go开发,下面的是学习主要参考文档,强烈建议多看几遍
Go语言101 :https://gfw.go101.org/article/101-about.html
本文分享自微信公众号 - 数据咖(bigbatacoffee)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。