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次。