- 轻量级 "线程"
- 非抢占式多任务处理,由协程主动交出控制权
- 编译器/解释器/虚拟机层面的多任务
- 多个协程可以在一个或多个线程上运行
goroutine 的定义
- 任何函数只需加上 go 就能送给调度器运行
- 不需要再定义时区分是否是异步函数
- 调度器再合适的点进行切换(go是在固定的点进行qie'huan)
- 使用-race来检测数据访问冲突
goroutine 可能的切换点
- I/O,select
- channel
- 等待锁
- 函数调用(有时)
- runtime.Gosched()
- 只是参考,不能保证切换,不能保证在其他地方不切换
不管开多少个 goroutine 它都会映射到CPU上去,有几个核数,就最多几个线程
系统是吧很多个协程映射到一个或多个线程里
channel
goroutine与goroutine之间的双向通道叫channel
channel语法
package main
func chanDemo(){
c:=make(chan int)
go func(){
for {
n:= <-c
fmt.Println(n)
}
}()
c <- 1
c <- 2
time.Sleep(time.Millisecond)
}
func main(){
chanDemo()
}
结果
1
2
channel作为参数,数组类型例子(一等公民)
package main
import (
"fmt"
"time"
)
func worker(id int, c chan int) {
for {
//n := <-c
fmt.Printf("Worker %d recevied %c\n", id, <-c)
}
}
func chanDemo() {
var channels [10]chan int
for i := 0; i < 10; i++ {
channels[i]= make(chan int)
go worker(i, channels[i])
}
for i := 0; i < 10; i++ {
channels[i] <-'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A'+ i
}
time.Sleep(time.Microsecond)
}
func main() {
chanDemo()
}
channel作为返回值
package main
import (
"fmt"
"time"
)
func createWorker(id int) chan<- int{
c:=make(chan int)
go func() {
for {
fmt.Printf("Worker %d recevied %c ID %d \n",id, <-c,&c)
}
}()
return c
}
func chanDemo() {
var channels [10]chan<- int
for i := 0; i < 10; i++ {
channels[i]= make(chan int)
channels[i]=createWorker(i)
}
for i := 0; i < 10; i++ {
channels[i] <-'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A'+ i
}
time.Sleep(time.Microsecond)
}
func main() {
chanDemo()
}
运行结果
Worker 7 recevied h ID
Worker 0 recevied a ID
Worker 6 recevied g ID
Worker 9 recevied j ID
Worker 8 recevied i ID
Worker 5 recevied f ID
Worker 1 recevied b ID
Worker 1 recevied B ID
Worker 0 recevied A ID
Worker 3 recevied d ID
Worker 4 recevied e ID
Worker 2 recevied c ID
Worker 2 recevied C ID
Worker 3 recevied D ID
Worker 4 recevied E ID
Worker 5 recevied F ID
Worker 8 recevied I ID
因为在channel发送数据后协程的收发时候比较消耗资源,所以可以做一个缓冲区,当缓冲区满的时候再发送
package main
func worker(id int,c chan int){
go func(){
for {
fmt.Printf("Worker %d recevied %c \n",id,<-c)
}
}
}
func bufferedChannel(){
c:=make(chan int, 3)
worker(0,c)
c<-'a'
c<-'b'
c<-'c'
c<-'d'
time.Sleep(time.Millisecond)
}
func main(){
bufferedChannel()
}
运行结果
Worker 0 recevied a
Worker 0 recevied b
Worker 0 recevied c
Worker 0 recevied d
在程序中呢,我们被动的结束channel不是太好,所以我们主动结束掉
例子1
package main
func doworker(id int,c chan int){
for {
fmt.Printf("Worker %d recevied %c \n",id,<-c)
}
}
// 用循环来防止发生接受空串
func worker(id int,c chan int){
// 第一种防空字符串或者0值方法
go func() {
for n:=range c {
fmt.Printf("Worker %d recevied %c \n", id,n)
}
}()
// 第二种防空字符串或0值方法
go func() {
for {
n,ok:=<-c
if !ok{
break
}
fmt.Printf("Worker %d recevied %c \n", id,n)
}
}()
}
func channelClose(){
c:=mack(chan int)
// go doworker(0,c) // 没有
go worker(0,c)
c<-'a'
c<-'b'
c<-'c'
close(c)
time.Sleep(time.Millisecond)
}
结果
// 使用了doworker结果
Worker 0 recevied a
Worker 0 recevied b
Worker 0 recevied c
Worker 0 recevied d
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
Worker 0 recevied
// 使用了worker结果
Worker 0 recevied a
Worker 0 recevied b
Worker 0 recevied c
Worker 0 recevied d
在主动结束channel中,使用了time.Sleep所以在主动结束前,会接收channel会打印一秒的空字符串,整数型会打印0,所以,为了预防出现空字符串或者0值,我们用了range来预防或者if来防止空字符串和0值
但是在程序中,我们程序是在goroutine中执行的,我们怎么知道他执行完成了呢?我们加延时是不行的,所以我们这里需要在里面告诉外面我们执行完了,所以我们用通信,不要用共享内存来通信,我们用通信来共享内存
例子
package main
import (
"fmt"
"sync"
)
type worker struct {
in chan int
done func()
}
func doWorker(id int,w worker) {
go func() {
for n:=range w.in {
fmt.Printf("Worker %d recevied %c \n", id, n)
w.done()
}
}()
}
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
done: func() {
wg.Done()
},
}
go doWorker(id,w)
return w
}
func chanDome() {
var wg sync.WaitGroup
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i,&wg)
}
for i, worker := range workers {
worker.in <- 'a' + i
wg.Add(1)
}
//for _,worker:=range workers{
//
// <-worker.done
//
//}
for i, worker := range workers {
worker.in <- 'A' + i
wg.Add(1)
}
//for _,worker:=range workers{
//
// <-worker.done
//
//}
wg.Wait()
}
//func bufferedChannel(){
// c:=make(chan int, 3) //这里的3是缓冲区大小
// doWorker(0,c)
// c<-'a'
// c<-'b'
// c<-'c'
// c<-'d'
// time.Sleep(time.Millisecond)
//}
func main() {
chanDome()
//bufferedChannel()
}