Concurrency
- Goroutines 一个goroutines就是一个轻量(lightweight)的线程,语法很简单:
go f(x,y,z)
这表示创建一个线程来执行f(x,y,z)
函数。
**Note: **Goroutines run in the same address space, so access to shared memory must be synchronized.
- Channels 这个差不多就是管道(pipe)了。可以往里面写东西,也可以读东西。
ch <- v // send v to channel ch
v := <- ch // retrive from ch and assign value to v
和slice,map一样,channel必须使用make来初始化。
ch := make(chan int)
一段代码例子:
package main
import "fmt"
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
- Buffered Channels Channels可以带buffer。在
make()
函数中,创建channel的时候第二个参数表示buffer的长度。
ch := make(char int, 100)
往一个buffered channels发送数据的时候,当buffer是满的时候会block。在获取数据的时候,当buffer是空的时候会Block。
- Range and Close 发送者可以关闭channels来表明已经没有数据要发送了。Receivers能够测试channel是否被关闭。语法如下:
v,ok := <-ch
如果channels被关闭了,那么ok=false。可以使用for i := range c
来从一个channel中读取数据,直到channel被关闭。
Note:Only the sender should close a channel, never the receiver. Sending on a closed channel will cause a panic.
- Select select 语句用于让goroutine等待多个通信操作。一个
selec
会block直到其中一种case可以运行。如果有多种情况都可以可以执行的时候,随机来执行其中一种。官网,没有特别明白select的用处。
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}
- sync.Mutex 这个就是互斥了(mutual exclusion),官网Doc没有讲到如何在go中实现差不多join的函数。用了
time.sleep
来等待goroutines运行结束。下面使用一个链表的插入来演示其作用。
type Node struct {
value int
next *Node
}
type LinkeList struct {
mu sync.Mutex
head *Node
len int
}
func initList() *LinkList {
return &LinkedList{sync.Mutex{},nil,0}
}
func (list *LinkedList) inertNode(value int) {
var = Node{value,int}
list.mu.Lock() //上锁
newNode.next = list.head
list.head = &newNode
list.len++
list.mu.Unlokc() //解锁
}
func (list *LinkedList) printList() {
var temp = list.head
for temp != nil {
fmt.Println(temp.value)
temp = temp.next
}
}
func intserArray(list *LinkedList) {
var nums = [1000]int
for i:= 0; i < 1000; i++ {
nums[i] = i
}
for _,v := range nums {
list.insertNode(v)
}
}
func main() {
var list = initList()
go insterArray(list)
go insertArray(list)
time.sleep(time.Second) //不用join,而是用sleep的方式来等待主线程运行结束
fmt.Println(list.len)
}
上述代码,如果不加锁的话,最后输出的链表长度会不等于2000。上锁才可以保持同步。