协程 Coroutine
- 轻量级 “线程”
- 非抢占式多任务处理,由协程主动交出控制权
- 编译器、解释器、虚拟机层面的多任务
- 多个协程可能在一个或多个线程上运行
非抢占式任务处理实例
package main
import (
"fmt"
"time"
)
func main() {
for i:= 0; i < 10; i++ {
go func(i int) {
for {
fmt.Printf("Hello from" +
"goroutine %d\n", i)
}
}(i)
}
time.Sleep(time.Millisecond)
}
go run goroutine.go
go run -race goroutine.go //数据冲突检测
- 子程序是协程的一个特例
其他语言中的协程
-
C++ :Boost.Coroutine
-
java : 不支持
-
python
- 使用yield关键字实现协程
- python 3.5 加入了async def对协程原生支持
-
go
goroutine的定义 -
任何函数只需要加上go就能送给调度器运行
-
不需要在定义时区分是否是异步函数
-
调度器在合适的点进行切换
-
使用-race 来检测数据访问冲突
goroutine 可能得切换点
- I/O,select
- channel
- 等待锁
- 函数调用(有时)
- runtime.Gosched
- 只是参考,不能保证切换,不能保证在其他地方不切换
Channel
channel 是goroutine和goroutine之间的交互
channel的创建实例:
package main
import (
"fmt"
"time"
)
func channDemo() {
//var c chan int //c == nil
c := make(chan int)
/*channel创建之后需要有goroutine接收,
不然会产生死锁*/
go func() {
for {
n := <- c
fmt.Println(n)
}
}()
c <- 1
c <- 2
time.Sleep(time.Millisecond)
}
func main() {
channDemo()
}
channel能作为函数参数和返回值
func creatWorker(id int) chan<- int{
c := make(chan int)
go func() {
for {
fmt.Printf("Worker %d received %c\n",
id, <- c)
}
}()
return c
}
func channDemo() {
var channels [10] chan<- int
for i := 0; i < 10; i++ {
//创建10个channel
channels[i] = creatWorker(i)
}
//分别给10个channel发送数据
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
func main() {
channDemo()
}
channel
buffered channel
range
package main
import (
"fmt"
"time"
)
/*func worker(id int, c chan int) {
for {
fmt.Printf("Worker %d received %c\n",
id, <- c)
}
}*/
func worker(id int, c chan int) {
for n := range c{
/*n, ok := <- c
if !ok {
break
}*/
fmt.Printf("Worker %d received %d\n",
id, n)
}
}
func creatWorker(id int) chan<- int{
c := make(chan int)
go worker(id, c)
return c
}
/*
func channDemo() {
//var c chan int //c == nil
c := make(chan int)
//channel创建之后需要有goroutine接收,不然会产生死锁
go worker(0, c)
c <- 1
c <- 2
time.Sleep(time.Millisecond)
}*/
func channDemo() {
var channels [10] chan<- int
for i := 0; i < 10; i++ {
//创建10个channel
channels[i] = creatWorker(i)
}
//分别给10个channel发送数据
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
func bufferedChannel() {
//创建channel,缓冲区大小为3
c := make(chan int, 3)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
time.Sleep(time.Millisecond)
}
// 由发送发发送数据发送结束标志
func channelClose() {
c := make(chan int)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
close(c)
time.Sleep(time.Millisecond)
}
func main() {
fmt.Println("Channel as first-class citizen")
channDemo()
fmt.Println("Buffered channel")
bufferedChannel()
fmt.Println("Channel close and range")
channelClose()
}
CSP: 不要通过共享内存来通信,通过通信来共享内存
使用Channel等待任务结束
例一:使用Channel来等待goroutine结束
- waitGroup
package main
import (
"fmt"
"sync"
)
func doWorker(id int,
w worker) {
for n := range w.in{
fmt.Printf("Worker %d received %c\n",
id, n)
w.done()
}
}
type worker struct {
in chan int
//done chan bool
//wg *sync.WaitGroup
done func()
}
func creatWorker(id int , wg *sync.WaitGroup) worker{
w := worker{
in : make(chan int),
done: func() {
wg.Done()
},
}
go doWorker(id, w)
return w
}
func channDemo() {
var wg sync.WaitGroup
var workers [10] worker
for i := 0; i < 10; i++ {
//创建10个channel
workers[i] = creatWorker(i, &wg)
}
wg.Add(20)
//分别给10个channel发送数据
for i, worker := range workers{
worker.in <- 'a' + i
//<-workers[i].done
}
for i , worker := range workers{
worker.in <- 'A' + i
//<-workers[i].done
}
wg.Wait()
}
func main() {
fmt.Println("Channel as first-class citizen")
channDemo()
}
使用Channel实现树的遍历
#main.go
package main
import (
"fmt"
"awesomeProject/11-3-tree-traverse"
)
func main() {
var root tree_11.TreeNode
root = tree_11.TreeNode{Value:3}
root.Left = &tree_11.TreeNode{}
root.Right = &tree_11.TreeNode{5,nil,nil}
root.Left.Right = tree_11.CreatNode(2)
root.Right.Left = new(tree_11.TreeNode)
root.Right.Left.SetValue(4)
root.Traverse()
nodeCount := 0
root.TraverseFunc(func(n *tree_11.TreeNode) {
nodeCount++
})
fmt.Println("Node count:", nodeCount)
c := root.TraverseWithChannel()
maxNode := 0
for node := range c{
if node.Value > maxNode {
maxNode = node.Value
}
}
fmt.Println("Max Node value:", maxNode)
}
#node.go
package tree_11
import "fmt"
type TreeNode struct {
Value int
Left,Right *TreeNode
}
func CreatNode(value int) *TreeNode {
return &TreeNode{Value:value}
}
/*结构体得方法*/
func (node TreeNode) Print() {
fmt.Println(node.Value)
}
func (node *TreeNode) SetValue(value int) {
node.Value = value
}
func (node *TreeNode) Traverse() {
node.TraverseFunc(func (n *TreeNode) {
n.Print()
})
fmt.Println()
}
func (node *TreeNode) TraverseFunc(f func(*TreeNode)) {
if node == nil {
return
}
node.Left.TraverseFunc(f)
f(node)
node.Right.TraverseFunc(f)
}
func (node *TreeNode) TraverseWithChannel() chan *TreeNode {
out := make(chan *TreeNode)
go func() {
node.TraverseFunc(func(node *TreeNode) {
out <- node
})
close(out)
}()
return out
}