Go 语言编程 —— 平台研发部 吴植民
目录 为什么我们需要Go语言 1 Go语言的简介 2 Go语言语法 3 面向对象编程 4 并发编程 5 2
一、为什么我们需要Go语言
为什么我们需要Go语言 是什么促使Go的出现 随着机器性能的提升、软件规模与复杂度的提高,Java逐步取代了单机时代的编程之王C语言的位置。然后Java编程的体验并未尽如人意。历年来的编程语言排行榜显示,Java语言的市场份额在逐步下跌。Go语言此时应运而生,Go语言官方自称Go语言的出现是因为“近10年来开发程序之难让我们有点沮丧”。Go希望成为互联网时代的C语言。 4
互联网时代的C语言需要考虑哪些关键问题呢 为什么我们需要Go语言 互联网时代的C语言需要考虑哪些关键问题呢 并行与分布式支持 多核化和集群化是互联网时代的典型特征。 软件工程支持 互联网时代C语言需要考虑软件品质保障和团队协作相关的话题 编程哲学的重塑 互联网时代C语言需要回答什么才是最佳的编程实践这个问题。 5
并发执行的“执行体”。Go语言在语言级别支持协程(微线程)。多数语言在语法层面并不直接支持协程,而通过库的方式支持协程的功能也不完整。 并行与分布式支持 并发执行的“执行体”。Go语言在语言级别支持协程(微线程)。多数语言在语法层面并不直接支持协程,而通过库的方式支持协程的功能也不完整。 执行体间的互斥和同步。Go语言提供协程之间的互斥和同步。 执行体间的消息传递。多数语言在并发编程模型上选择了共享内存模型,而Go语言选择了消息队列模型。 软件工程支持 Go语言可能是第一个将代码风格强制统一的语言。 编程哲学的重塑 Go语言用批判吸收的眼光,融合众家之长,但时刻警惕特性复杂化,极力维持语言特性的简洁,力求小而精。 Go语言反对函数和操作符重载 Go语言放弃构造和析构函数 Go语言支持类、类成员方法、类的组合,但反对继承,反对虚函数和虚函数重载 6
提升现有编程语言对程序库等依赖性(dependency)的管理。 解决多处理器的任务 为什么我们需要Go语言 Go语言的目标 提升现有编程语言对程序库等依赖性(dependency)的管理。 解决多处理器的任务 Go语言的特色 简洁、快速、安全、并行、有趣 、开源、支持泛型编程、内存管理、数组安全、编译迅速。 7
二、Go语言的简介
Go语言的简介 Go语言简史 Go语言是由贝尔实验室包括肯·汤普森在内的Plan 9原班人马开发。Go语言的第一个版本在2009年11月正式对外发布,并在此后的两年内快速迭代,发展迅猛。第一个正式版本在2012年3月28正式发布。 9
Go语言的简介 Go语言特性 自动垃圾回收 更丰富的内置类型 函数多返回值 错误处理 匿名函数和闭包 类型和接口 并发编程 反射 语言交互性 1010
三、Go语言语法
Go语言语法 变量 变量声明 var v1 int var v2 string var v3 [10] int var ( v1 int ) 变量初始化 var v1 int = 10 //ok var v2 = 10 //ok v3 := 10 //ok v2 := 10 //error 1212
func GetName () (firstName, lastName, nickName string) { Go语言语法 变量 变量赋值 var v1 int v1 = 123 i, j = j, i //多重赋值,交换 匿名变量 func GetName () (firstName, lastName, nickName string) { return “May”, “Chan”, “Chibi Maruko” } _, _, nickName := GetName ()//仅获取nickName, _为匿名变量占位符 1313
Go语言预定义了true, false,iota 常量 字面常量 常量定义 预定义常量 Go语言预定义了true, false,iota const ( //iota重置为0 c0 = iota //c0为0 c1 = iota //c1为1 c2 = iota //c2为2 ) 枚举 const ( Sunday = iota Monday numberDays //这个常量未导出 ) 1414
整型 int8 byte int16 int uint uintptr 浮点类型 float32 float64 Go语言语法 类型 基础类型 布尔类型 bool 整型 int8 byte int16 int uint uintptr 浮点类型 float32 float64 复数类型 complex64 complex128 字符串 string 字符类型 rune 错误类型 error 复合类型 指针(pointer) 数组(array) 切片(slice) 字典(map) 通道(chan) 结构体(struct) 接口(interface) 1515
fmt.Printf (“default”) } Go语言语法 流程控制 条件语句 if a < 5 { return 0 } else { return 1 } 选择语句 switch i { case 0 : fmt.Printf (“0”) case 1 : fallthrough case 2, 3 : fmt.Printf (“2,3”) default: fmt.Printf (“default”) } 1616
case 0 <= Num && Num <= 3 : fmt.Printf (“0-3”) Go语言语法 流程控制 选择语句 switch { case 0 <= Num && Num <= 3 : fmt.Printf (“0-3”) case 4 <= Num && Num <= 6 : fmt.Printf (“4-6”) } 循环语句 //case 1 sum := 0 for i := 0; i < 10; i++ {//条件表达式中也支持多重赋值 sum += i } //case2 for { //相当于while,do-while sum++ //支持按标签break 1717
支持continue、break、goto,break可按标签选择中断到哪一个循环 流程控制 跳转语句 支持continue、break、goto,break可按标签选择中断到哪一个循环 1818
函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。 Go语言语法 函数 函数定义 函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句。 func Add (a int, b int) (ret int, err error) { if a < 0 || b < 0 { err = errors.New (“error”) return } return a + b , nil //多重返回值 匿名函数和闭包 匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数可以直接赋值给一个变量或直接执行。 闭包是可以包含自由变量的代码块。相当于Java中的嵌套匿名类 1919
大多数函数将error作为最后一个返回值 defer Go语言语法 错误处理 大多数函数将error作为最后一个返回值 defer defer语句的含义是不管程序是否出现异常,均在函数退出时自动执行相关代码。 func CopyFile (dst, src string) (w int64, err error) { srcFile, err := os.Open (src) if err != nil { return } defer srcFile.Close () //清理多语句,使用匿名函数 defer func () { //清理工作 } () 2020
四、面向对象编程
func (a Integer) Less (b Integer) bool { return a < b } 面向对象编程 类型系统 Go语言中的大多数类型都是值语义,并且都可以包含对应的操作方法。在需要的时候,你可以给任何类型(包括内置类型)增加新方法。而在实现某个接口时,无需从从该接口继承,只需要实现该接口要求的所有方法即可。 type Integer int func (a Integer) Less (b Integer) bool { return a < b } func main () { var a Integer = 1 if a.Less (2) { fmt.Println (a, “Less 2”) 2222
func (r *Rect) area () float64 { return r.Width * r.Height 面向对象编程 可见性 Go语言中要使某个符号对其他包(package)可见,只需要将该符号定义为以大写字母开头,否则不可见。成员方法和成员变量的可见性遵循同样的规则。Go语言中符号的可访问性是包一级的而不是类型一级的。 type Rect struct { X, Y float64 Width, Height float64 } func (r *Rect) area () float64 { return r.Width * r.Height 2323
在Go语言中,一个类只要实现了接口要求的所有函数,我们就说这个类实现了该接口。 这种方式有如下几种好处: Go语言不需要绘制类库的继承树图 面向对象编程 接口 非侵入式接口 在Go语言中,一个类只要实现了接口要求的所有函数,我们就说这个类实现了该接口。 这种方式有如下几种好处: Go语言不需要绘制类库的继承树图 实现类的时候,只需要关心自己应该提供哪些方法,不用纠结接口需要拆的多细才合适。 不用为了实现一个接口而导入一个包,减少耦合。 type File struct { // … } func (f *File) Read (buf [] byte) (n int, err error) func (f *File) Write (buf [] byte) (n int, err error) func (f *File) Close () error 2424
Read (buf [] byte) (n int, err error) 面向对象编程 接口 type IFile interface { Read (buf [] byte) (n int, err error) Write (buf [] byte) (n int, err error) Close () error } type IReader interface { type IWriter interface { type ICloser interface { var file1 IFile = new (File) var file2 IReader = new (File) var file3 IWriter = new (File) var file4 ICloser = new (File) 2525
五、并发编程
并发编程 协程 Go语言在语言级别支持轻量级线程(即协程),叫goroutine。Go语言提供的所有系统调用操作都会出让CPU给其他goroutine。这让事情变得非常简单,让轻量级线程的切换管理不依赖于系统的线程和进程,也不依赖于CPU的核心数量。 Go语言中最重要的一个特性是go关键字 func Add (x, y int) { z := x + y } go Add (2, 1) //并发执行 2727
并发编程 并发通信 工程上两种最常见的并发通信模型: 共享数据 消息 一个大的系统中具有无数的锁、无数的共享变量、无数的业务逻辑与错误处理分支。采用共享数据将是一场噩梦。Go语言已并发编程作为最核心优势,提供了以消息机制而非共享内存作为通信方式的通信模型(channel)。 channel是类型相关的的,一个channel只能传递一种类型的值,这个类型需要在声明的channel时指定。channel相当于一种类型安全的管道。 2828
一般channel的声明形式:var chanName chan ElementType 并发编程 并发通信 channel 一般channel的声明形式:var chanName chan ElementType 定义一个channel:ch := make (chan int) 将一个数据写入channel:ch <- value 从channel中读取数据:value := <- ch select Go语言直接在语言级别支持select关键字,用于处理异步IO问题。 select有比较多的限制,其中最大的限制就是每个case语句里必须是一个IO操作。 select { case <- chan1 : case chan2 <- 1 : default : } 2929
创建一个带缓冲的channel:c := make (chan int, 1024) 并发编程 并发通信 缓冲机制 创建一个带缓冲的channel:c := make (chan int, 1024) 超时机制 Go语言没有提供直接的超时处理机制,但我们可以利用select机制实现一套 timeout := make (chan bool, 1) go func () { time.Sleep (1e9) //等待1秒 timeout <- true } () select { case <- ch : case <- timeout : default : } 3030
Go语言中channel本身是一个原生类型,因此channel可以传递。 下面我们利用这个特性来实现*nix常见的管道特性 并发编程 并发通信 channel的传递 Go语言中channel本身是一个原生类型,因此channel可以传递。 下面我们利用这个特性来实现*nix常见的管道特性 type PipeData struct { value int handler func (int) int next chan int } func handler (queue chan *PipeData) { for data := range queue { data.next <- data.handler (data.value) 3131
我们在将一个channel变量传递到一个函数时,可以通过将其制定为单向channel变量,从而限制该函数中可以对此channel的操作。 并发编程 并发通信 单向channel 我们在将一个channel变量传递到一个函数时,可以通过将其制定为单向channel变量,从而限制该函数中可以对此channel的操作。 var ch1 chan int //ch1是一个正常的channel,不是单向的 var ch2 chan<- float64 //ch2是单向的,只用于写float64数据 var ch3 <-chan int //ch3是单向的,只用于读取int数据 //单向channel与双向channel之间的转换 ch4 := make (chan int) ch5 := <-chan int (ch4) //ch5为单向的读取channel ch6 := chan<- int (ch4) //ch6为单向的写入channel 3232
我们可以通过设置环境变量GOMAXPROCS的值来控制使用多少个CPU核心。 runtime.GOMAXPROCS (16) 并发编程 多核并行化 我们可以通过设置环境变量GOMAXPROCS的值来控制使用多少个CPU核心。 runtime.GOMAXPROCS (16) runtime包中还提供了一个函数NumCPU ()来获取CPU核心数。 全局唯一性操作 Go语言提供了一个Once类型来保证全局的唯一性操作。Once的Do ()方法可以保证在全局范围内只调用指定的函数一次,而且所有其他goroutine在调用到此语句时,将会先被阻塞,直至全局唯一的Once.Do ()调用结束才继续。 3333
没有Q&A 34