您的位置:首页 > 编程语言 > Go语言

Golang学习摘录(二)

2015-11-30 16:36 393 查看
Golang学习二

Full slice expressions切片操作

Python a[low: high: direction]

比如 a = [1, 2, 3], a[1:2:-1]负1表示反方向,结果是[3,2]

Golang第三个不是表示方向:a[low : high : max],string类型切片不支持max操作。

max参数用来指定返回的切片的容量,slice在golang里底层是数组,并且有默认初始大小cap(a)。指定max参数后,返回的slice底层数组将不再是默认大小,而是max-low大小.

所以有如下约定: 0 <= low <= high <= max <= cap(a)

a := [5]int{1, 2, 3, 4, 5}

t := a[1:3:9]

此时切片t类型是[]int,长度length是2,容量是9-1等于8.意思是说,t底层数组长度为8,后续对t做append操作,只要大小不超过8,不必重新分配地址。

Type assertions

go的类型判断有点特别,v, ok := x.(T)断言x不是nil并且x类型是T。如果类型判断正确,ok为true,v为x转化为T后的值。如果失败,ok为false,v是T类型的zero_value.

协程入门

如果有Python功底,对协程coroutine很熟悉,理解goroutine分分钟的事。goroutine与coroutine两个单词长这么像,就是因为本来就一回事。不理解也没事,先看看协程。

greenlet:python协程基础框架,很原始,是纯手工的协程框架

from greenlet import greenlet
def test1():
print 12
gr2.switch()
print 34
def test2():
print 56
gr1.switch()
print 78
gr1 = greenlet(test1)
gr2 = greenlet(test2)#创建两个协程test1,test2,但并未启动
gr1.switch()#切换到gr1,即启动协程gr1


创建两个协程(greenlet框架叫做greenlet),分别是test1,test2,但是并未启动两个协程。然后主协程执行gr1.switch(),CPU执行权交给协程test1,输出12,然后切换到协程test2输出56,然后CPU执行权又切回test1,输出34。由于此时并没有切换到test2,78不会被输出,程序结束。

上面就是协程入门,可以看到greenlet所有切换工作必须由代码显示执行,框架不会自动调度,所以说greenlet是纯手工的框架。协程跟多线程有个最大区别:某个协程如果不通过switch交出CPU使用权,其他协程无法获得CPU。在一个线程内部,包含着很多协程,当该线程获得CPU时,该线程内的switch进来(获得CPU)但是还没有switch出去(交出CPU)的协程获得CPU,其他协程只能等待它switch交出CPU使用权。

gevent是比greenlet高级的协程框架,前面说了,greenlet的协程切换时纯手工,原始,那么gevent就是高级的:gevent会自动创建一个主协程,级别比较高,我们叫做调度器,它会自动调度该线程内的很多其他协程,当某个协程阻塞住,强行要回CPU使用权,给其他没有阻塞住的协程使用,如果所有其他的协程都阻塞,它就自个拿着CPU使用权,轮询其他协程,看谁不阻塞了,再交给谁。gevent能让程序员像写多线程一样写多协程,切换,调度,框架都做好了,并且接口与多线程类似,学习难度低。

goroutine

前面说了协程,golang的goroutine库就是更高级的gevent,也是自动调度,想要goroutine同步就要使用channel或者其他互斥元,与多线程编程十分类似。

unbuffered channel可以用来同步goroutine

buffered channel可以用作counting semaphore,可以参考上一篇golang学习文章里的信号量限流的例子

sync包里提供了sync.Mutex or sync.RWMutex两种互斥元。说直白点就是锁,获取锁,释放锁从而同步

sync.Once用来保证一堆goroutine执行同一个函数,只会执行一次,其他的阻塞直到那一次执行结束

var a string
var once sync.Once
func setup() {
a = "hello, world"
}
func doprint() {
once.Do(setup)
print(a)
}
func threeprint() {
go doprint()
go doprint()
go doprint()
}

虽然起了3个goroutine去执行setup,但是once.Do保证只会有一个goroutine执行一次setup,其他goroutine等待它setup结束,然后打印3次。

注意闭包的调用

在循环中调用函数或者goroutine方法,一定要采用显示的变量调用,不要在闭包函数里面使用循环的变量

for i:=0; i<limit; i++{
go func(){ DoSomething(i) }() //错误的做法
go func(i int){ DoSomething(i) }(i)//正确的做法
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: