Go 并发
1. 线程
开始线程只需要使用go关键字即可
2. 线程等待
主线程需要所有子线程执行完成后才结束
线程等待包 sync.WaitGroup
添加等待线程数 waitGroup.Add(100)
子线程结束标志 waitGroup.Done()
主线程等待标志 waitGroup.Wait()
import (
"fmt"
"sync"
)
func main() {
var waitGroup sync.WaitGroup
// 添加需要等待线程的数量
waitGroup.Add(100)
for i := 0; i < 100; i++ {
// 开启线程
go func(x int) {
// defer定义的每次执行完成之前减少线程等待数量
defer waitGroup.Done()
fmt.Println(x)
}(i)
}
// 等待子线程结束 在执行后面的逻辑
waitGroup.Wait()
}
3.线程锁
防止线程中重要逻辑还没完成就切换线程导致的错误
- sync.Mutex
import (
"fmt"
"sync"
)
var waitGroup sync.WaitGroup
var lock sync.Mutex
var total int32
func main() {
waitGroup.Add(2)
go add()
go sub()
waitGroup.Wait()
fmt.Println(total)
}
func add() {
defer waitGroup.Done()
for i := 0; i < 100000; i++ {
// 计算前加锁
lock.Lock()
total += 1
// 计算后解锁
lock.Unlock()
}
}
func sub() {
defer waitGroup.Done()
for i := 0; i < 100000; i++ {
lock.Lock()
total -= 1
lock.Unlock()
}
}
- sync.RWMutex
读写锁 var lock sync.RWMutex
lock.Lock() lock.Unlock()写锁会阻塞其他线程的度和写
lock.RLock() lock.RUnlock()读锁不会阻塞其他线程的读
- atomic原子锁
需要传指针类型
import (
"fmt"
"sync"
"sync/atomic"
)
var waitGroup sync.WaitGroup
var lock sync.Mutex
var total int32
func main() {
waitGroup.Add(2)
go add()
go sub()
waitGroup.Wait()
fmt.Println(total)
}
func add() {
defer waitGroup.Done()
for i := 0; i < 100000; i++ {
// 需要注意的是这块的值是指针
atomic.AddInt32(&total, 1)
}
}
func sub() {
defer waitGroup.Done()
for i := 0; i < 100000; i++ {
// 需要注意的是这块的值是指针
atomic.AddInt32(&total, -1)
}
}
4. channel 线程间通信
- channel定义
// var 变量名称 chan channel内部类型
var msg chan string
// 空间为0,无缓冲类型,适用于消息通知
msg = make(chan string, 0)
// 空间大于0,有缓冲类型,适用于生产者和消费者之间的通信
msg = make(chan string, 1)
- channel读写
msg := make(chan string, 1)
// 写数据
msg <- "向channel中写数据"
// 读数据
str := <-msg
- 循环channel
循环channel用for
关闭channel用close
func main() {
msg = make(chan string, 1)
go func(msg chan string) {
// for 循环一直取msg中的内容,会一直阻塞在这块
for data := range msg {
fmt.Println(data)
}
fmt.Println("all done")
}(msg)
for i := 0; i < 10; i++ {
time.Sleep(time.Millisecond * 500)
msg <- strconv.FormatInt(int64(i), 10)
}
// 关闭channel,上面的for循环就会退出
close(msg)
time.Sleep(time.Second * 10)
}
示例:字母数字交替打印AB12CD34EF56GH78IJ910KL1112MN1314OP1516QR1718ST1920UV2122WX2324YZ2526
import (
"fmt"
"time"
)
var num, word = make(chan bool), make(chan bool)
func printNum() {
i := 1
for {
<-num
fmt.Printf("%d%d", i, i+1)
i += 2
word <- true
}
}
func printWord() {
i := 65
for {
<-word
if i > 90 {
return
}
fmt.Printf("%c%c", i, i+1)
i += 2
num <- true
}
}
func main() {
go printNum()
go printWord()
word <- true
time.Sleep(time.Second * 5)
}
- 单向channel
// 只能读取
var reader <-chan string
// 只能写入
var writer chan<- string
reader = make(<-chan string)
writer = make(chan<- string)
示例
func main() {
// channel定义时可以是双向channel
ch := make(chan string, 0)
// 函数定义时直接定义单向channel
go func(reader <-chan string) {
for msg := range reader {
fmt.Println(msg)
}
}(ch)
// 函数定义时直接定义单向channel
go func(writer chan<- string) {
for i := 0; i < 10; i++ {
time.Sleep(time.Millisecond * 500)
writer <- "写入中"
}
close(ch)
}(ch)
time.Sleep(time.Second * 5)
}
- select对多个channel的监控
import (
"fmt"
"time"
)
func func1(ch chan string) {
time.Sleep(time.Second)
ch <- "done"
}
func func2(ch chan string) {
time.Sleep(2 * time.Second)
ch <- "done"
}
func main() {
var ch1, ch2 = make(chan string), make(chan string)
go func1(ch1)
go func2(ch2)
// 这块会生成一个channel,5秒之后就会往里添加数据,用来超时的
t := time.NewTimer(time.Second * 5)
// 主要代码在这
for {
select {
case <-ch1:
fmt.Println("ch1 down")
case <-ch2:
fmt.Println("ch2 down")
case <-t.C:
fmt.Println("超时")
return
default:
time.Sleep(time.Millisecond * 100)
fmt.Println("其他通道没有值")
}
}
time.Sleep(6)
}
- context
context是个树型结构上下文,返回的context可以感知到父context退出,最顶层context为context.Background()
-
- 主动退出型context
ctx, exit := context.WithCancel(context.Background())
import (
"context"
"fmt"
"time"
)
func func1(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("接收到主函数退出通知")
return
default:
time.Sleep(time.Second)
fmt.Println("等待主函数发送结束消息")
}
}
}
func main() {
ctx, exit := context.WithCancel(context.Background())
go func1(ctx)
time.Sleep(time.Second * 2)
// 手动调用上面返回的exit,这会给ctx发个信号
exit()
time.Sleep(time.Second)
}
-
- 超时退出型context
ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
import (
"context"
"fmt"
"time"
)
func func1(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("接收到主函数退出通知")
return
default:
time.Sleep(time.Second)
fmt.Println("等待主函数发送结束消息")
}
}
}
func main() {
// 这里的ctx2秒就会退出
ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
go func1(ctx)
time.Sleep(time.Second * 5)
}
-
- 指定时间退出型context
ctx, _ := context.WithDeadline(context.Background(), time.Now())
import (
"context"
"fmt"
"time"
)
func func1(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("接收到主函数退出通知")
return
default:
time.Sleep(time.Second)
fmt.Println("等待主函数发送结束消息")
}
}
}
func main() {
// 这个会在指定的时间退出
ctx, _ := context.WithDeadline(context.Background(), time.Now())
go func1(ctx)
time.Sleep(time.Second * 5)
}
-
- 传值型context
ctx1, _ := context.WithTimeout(context.Background(), time.Second*2) ctx2 := context.WithValue(ctx1, "ping", "pong")
import (
"context"
"fmt"
"time"
)
func func1(ctx context.Context) {
// 这块接收到的值
fmt.Println(ctx.Value("ping"))
for {
select {
case <-ctx.Done():
fmt.Println("接收到主函数退出通知")
return
default:
time.Sleep(time.Second)
fmt.Println("等待主函数发送结束消息")
}
}
}
func main() {
ctx1, _ := context.WithTimeout(context.Background(), time.Second*2)
// 这里可以使用上面返回的超时context,在不影响超时退出的情况下,增加了传值
ctx2 := context.WithValue(ctx1, "ping", "pong")
go func1(ctx2)
time.Sleep(time.Second * 5)
}
初学linux,每学到一点东西就写一点,如有不对的地方,恳请包涵!