本文记录了我在学习Go的过程时的一些笔记,主要是比较Python和Go之间的差别并做简单描述,以此使Python程序员对Go语言的特性有简略的了解。初学不免有纰漏,欢迎各位批评指正补充交流,谢谢。程序员
Go中的数组须要在建立时肯定长度,一个更灵活的对象是slice,后者可使用append添加,二者的定义方式类似。数据库
var StrArray [10]string //数组,长度为10 var StrSlice []string //slice
slice能够根据现有的数组(称为底层数组)建立,但对其的修改会致使底层数组的改变。编程
Go语言支持指针,用法和C同样数组
结构体和Python中的Class类似,但在这一代码段中只能定义类型的数据布局,方法须要定义指定接收对象的函数(见“方法”)。多线程
type Point struct{ X int Y int }
在结构体中添加结构体成员会使变量的访问变得麻烦,Go中能够不带名称定义结构体成员称为匿名成员。并发
结合匿名成员以及方法对匿名成员的处理(包含某个结构体匿名成员的结构体能够接收该结构体的方法),匿名成员机制能够视为继承app
type ColoredPoint struct { Point // 匿名成员 color string } var cp ColoredPoint cp.X = 1 cp.Y = 2 cp.color = "red"
不一样于普通的函数,方法是指定接收对象的。iphone
包含某个结构体匿名成员的结构体能够接收该结构体的方法。异步
隐式实现:知足接口所需的方法即为实现某个接口,无需显式声明函数
type Phone interface { call()() text(str []string)(n int) }
当某一个类型拥有如上所属的输入和输出的Write方法时,便可称其实现了Writer接口。
type iPhone struct{} func (p iPhone) call (){ fmt.Println("call from iPhone") } func (p iPhone) text (str []string){ fmt.Println(str) fmt.Println("text from iPhone") return len(str) }
接口能够被做为一个变量定义,可被赋予具体类型。
var phone Phone // 赋值方法一 var iphone iPhone phone = iphone phone.call() phone.text("test") // 赋值方法二 phone = new(iPhone) phone.call() phone.text("test")
Go中每个并发的活动称为goroutine,不一样于Python虚假的多线程或不稳定的多进程,goroutine被归类为协程(Coroutine)。
略
go f()
不一样于Python会自动等待各Process运行结束后退出,在Go中main函数返回时,全部的goroutine都暴力地终结,可使用下文说起的通道阻塞或者sync的WaitGroup等待以保证各goroutine运行。
通道用于goroutine间的通讯,不一样于Python的Threading库或multiporcessing库中的Queue(队列),Go中的通道是须要标注数据类型的。
ch := make(chan int) //定义通道,int为数据类型 ch <- x // 发送数据 x = <- ch // 接收数据 <- ch // 接收数据并丢弃 close(ch) //关闭通道
对通道的收发操做都是阻塞的。
不一样于Queue关闭后没法收发,通道关闭后没法发送,但能够接收剩余的数据。
ch1 := make(chan int) ch2 := make(chan int, 0) // 二者含义相同
如上定义的通道,为无缓冲通道,即一次不阻塞的发送后,数据被接收以前,第二次发送被阻塞。
ch := make(chan int, 3) //定义通道,int为数据类型,容量为3
如上定义的通道,能够进行四次不阻塞的发送,第五次发送被阻塞(没有接收的前提下)。
为了不误用能够在函数的参数定义时固定通道的方向
func f(in <-chan int, out chan<- int) {}
如上定义时,通道in对于函数f来讲是只能接收的通道,通道out对于函数f来讲是只能发送的通道。
select的相似于switch,但不一样的是select的分支上是阻塞着的操做而非数据。select使能够同时等待多个操做的阻塞,直到某一个分支上的操做再也不阻塞。每一个select只执行一个分支。
select { case x1 <-ch1: // ... case x2 <-ch2: // ... case ch3 <- x3: // ... default: // ... }
一句任何涉及并发的编程都应该遵照的话:
‘‘Do not communicate by sharing memory; instead, share memory by communicating.’’
不要经过共享内存来通讯,应该用通讯来共享内存。即应当将对象限制在顺序执行的环境下(好比某个协程中)进行写操做。
也能够用锁。
相似multiprocessing.Lock有acquire()和release(),sync.Mutex有Lock()和Unlock()。(记得用defer延迟执行Unlock()以保证解锁的执行)
Go提供共了一种更复杂的锁,除了不可并行的写锁Lock()和Unlock(),还有可并行的读锁RLock()和RUnlock()。其使用相似于数据库的2、三级封锁协议。
延迟初始化,Once函数以某个函数为参数,保证这个只须要执行一次的函数在并行状况下执行且只执行一次。相同效果虽然用RWMutex也能够实现但Once更加简便
输出一份包含全部数据竞态的报告,go run/build/test时添加-race可使用该功能。
肯定须要使用的OS线程数目,能够在做为环境变量设置,或用函数runtime.GOMAXPROCS控制。
《Go程序设计语言》