参考书:Go语言编程
1 Go语言支持协程goroutine
2 协程的简单例子
var counter int = 0 func Count(lock *sync.Mutex){ lock.Lock() // 加锁 counter ++ fmt.Println(counter) lock.Unlock() // 解锁 } func main(){ lock := &sync.Mutex{} for i := 0;i<10;i++{ go Count(lock) } for{ lock.Lock() c := counter lock.Unlock() runtime.Gosched() if c >= 10{ break } } }
3 Go语言的通信方式channel,channel可以在两个或多个goroutine之间传递消息。
4 channel是类型相关的,一个channel只能传递一种类型的值,这个类型需要在声明channel时指定。
5 channel的一般声明形式为var chanName chan ElementType,ElementType为元素类型,如下
var ch chan int
var m map[string] chan bool
6 channel的定义也可使用make( ),如下
ch := make(chan int) // 声明并初始化了一个int类型的名为ch的channel
7 channel写入和读出
ch <- value // 将value值写入ch
value := <- ch // 读取ch值赋值给value
8 select语句,用于监听IO操作,当IO操作执行时会触发。每个case语句都必须是一个面向channel的操作,如下
select {
case <- chan1 :
// 如果chan1成功读到数据则进行该case处理语句
case chan2 <- 2 :
// 如果成功向chan2写入数据则进行该case处理语句
default :
// 如果上面都没有执行成功则进入default处理流程
}
9 select语句的语法
- 每个case都必须是一个通信
- 如果有多个case都可以运行,select会随机选择一个执行,其他的不会执行
- 如果没有case可执行,则去执行default,如果没有default,则会阻塞,直到有个case可以执行
10 创建一个带缓冲的channel
c := make(chan int , 1024) // 创建了一个大小为1024的int类型channel,即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞
11 channel的超时机制,常见的是利用select机制,例如
timeout := make(chan bool,1) go func(){ time.Sleep(1e9) timeout <- true }() select { case <- ch: fmt.Println("ch") case <- timeout: fmt.Println("timeout") }
12 单向channel变量,channel是一个原生类型,不仅支持被传递,也支持类型转换
var ch1 chan int // 不是单向channel
var ch2 chan<- float64 // 单向channel,只用于写float64数据
var ch3 <-chan int // 单向channel,只用于读取int数据
ch4 := make(chan int)
ch5 := <-chan int(ch4) // 强制转换为一个单向的读取channel
ch6 := chan<- int(ch4) // 强制转换为一个单向的写入channel
用法如下
func Parse( ch <-chan int) { // 限制只能读取channel
for value := range ch {
fmt.Println("Parsing value :" , value)
}
}
13 关闭channel,用内置函数close()即可,如close(ch),如果要判断一个channel是否已经关闭,可以使用多重返回值,如下
x , ok := <-ch
14 同步锁,Go语言的sync包提供了两种锁类型:sync.Mutex和sync.RWMutex。Mutex是简单的锁,当goroutine获取Mutex后其他goroutine只能等这个goroutine释放了该Mutex。RWMutex是单写多读锁,在读锁占用时会阻止写,但不阻止读,其他goroutine可以获取读锁(调用RLock())。而写锁(调用Lock())会阻止其他goroutine读和写的所有操作
15 对于这两种锁类型,任何一个Lock()或RLock()均需要保证对应有Unlock()或RUnlock()调用与之对应,否则可能导致等待该锁的所有goroutine处于饥饿状态,甚至可能导致死锁。
var l sync.Mutex
func foo() {
l.Lock()
defer l.Unlock()
// toDo
}
16 全局唯一性操作,即只需要运行一遍代码,如全局初始化操作,Go语言提供了一个Once类型来实现
var a string var once sync.Once func setup(){ a = "hello,world" } func doprint(){ once.Do(setup) fmt.Println(a) } func twoprint(){ go doprint() go doprint() }
once的Do()可以保证在全局范围内只调用指定函数一次,即setup( ),而且其他goroutine在调用此语句时会先被阻塞,直至全局中once.Do( )执行完才能继续