Goroutine

轻量级线程,goroutine是Go运行时(runtime)管理的协程,比操作系统线程更轻量

初始栈空间只有几kb,会按需自动增长

Go runtime提供M:N调度模型,N个Goroutine映射到M个操作系统线程

在函数调用前加go关键词启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import (
"fmt"
"time"
)

func work(id int){
for i:=0;i<3;i++{
fmt.Printf("Worker %d: %d\n",id,i)
time.Sleep(time.Millisecond*500)
}
}

func main(){
go work(1) //开启一个Goroutine
go work(2) //再开一个Goroutine
time.Sleep(2*time.Second)//主协程等待
}

Channel

Channel是Go提供的”通信管道”,用于在多个Goroutine之间传递数据

  • 安全:多Goroutine并发访问channel不需要额外加锁

  • 阻塞:

    • 发送数据时,如果没有Goroutine接收,会阻塞
    • 接收数据时,如果没有Goroutine发送,也会阻塞

声明与使用如下

1
2
3
4
5
6
7
8
9
package main
import "fmt"
func main(){
ch:=make(chan int,2)//带缓冲区的channel,容量为2
ch<-10
ch<-20
fmt.Println(<-ch)//10
fmt.Println(<-ch)//20
}

常见模式:
1.生产者-消费者模型
2.任务发布
3.同步信号


Select

多路复用,select语句用来同时监听多个channel的操作,哪一个先准备好就执行哪一个分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
import (
"fmt"
"time"
)

func main(){
ch1:=make(chan string)
ch2:=make(chan string)

go func(){
time.Sleep(1*time.Second)
ch1<-"Hello from ch1"
}()

go func(){
time.Sleep(2*time.Second)
ch2<-"Hello from ch2"
}()

for i:=0;i<2;i++{
select{
case msg1:=<-ch1:
fmt.Println(msg1)
case msg2:<-ch2:
fmt.Println(msg2)
case <-time.After(1500*time.Millisecond)://超时控制
fmt.Println("timeout")
}
}
}

Context

context是Go1.7引入的,用来在Goroutine之间传递取消信号、超时控制、元数据

常见函数:

  1. context.Background():根Context
  2. context.WithCancel():手动取消
  3. context.WithTimeout():超时自动取消
  4. context.WithDeadline():设定截止时间
  5. ctx.Done():返回一个channel,收到信号表示被取消
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main
import(
"fmt"
"time"
"context"
)
func worker(ctx context.Context){
for{
select{
case <-ctx.Done():
fmt.Println("worker stopped",ctx.Err())
return
default:
fmt.Println("working...")
time.Sleep(500*time.Millisecond)
}
}
}

func main(){
ctx,cancel:=context.WithTimeout(context.Background(),2*time.Second)
defer cancel()
go worker(ctx)
time.Sleep(3*time.Second)
}

2秒后ctx自动取消,Goroutine停止


sync.Mutex

互斥锁用于保证同一时间只有一个Goroutine能访问共享资源

并发写共享资源会产生数据竞争,需要加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main
import (
"fmt"
"sync"
)

var (
counter int
mu sync.Mutex
)

fun add(wg *sync.WaitGroup){
defer wg.Done()
for i:=0;i<1000;i++{
mu.Lock()
counter++
mu.Unlock()
}
}

func main(){
var wg sync.WaitGroup
wg.Add(2)
go add(&wg)
go add(&wg)
wg.Wait()
fmt.Println("Final counter:",counter)
}