Go语言学习之 Day08 并发编程
并发编程基本概念
并发编程开发将一个过程按照并行算法拆分为多个可以独立执行的代码块,从而充分利用多
核和多处理器提高系统吞吐率
顺序、并发与并行
- 顺序是指发起执行的程序只能有一个
- 并发是指同时发起执行(同时处理)的程序可以有多个(单车道并排只能有一辆车,可同时驶入路段多辆车)
- 并行是指同时执行(同时做)的程序可以有多个 (多车道并排可以有多个车)
例程
Go语言中每个并发执行的单元叫Goroutine,使用go关键字后接函数调用来创建一个Goroutine
- main函数也是由一个例程来启动执行,这个例程称为主例程,其他例程叫工作例程。主例程结束后工作例程也会随之销毁,使用sync.WaitGroup(计数信号量)来维护执行例程执行状态
- 可以通过runtime包中的GoSched让例程主动让出CPU,也可以通过time.Sleep让例程休眠从而让出CPU
闭包陷阱
因为闭包使用函数外变量,当例程执行是,外部变量已经发生变化,导致打印内容不正确,可使用在创建例程时通过函数传递参数(值拷贝)方式避免
共享数据
多个并发程序需要对同一个资源进行访问,则需要先申请资源的访问权限,同时再使用完成
后释放资源的访问权。当资源被其他程序已申请访问权后,程序应该等待访问权被释放并被
申请到时进行访问操作。同一时间资源只能被一个程序访问和操作
多个例程对同一个内存资源进行修改,未对资源进行同步限制,导致修改数据混乱
communication.go
package main
import (
"fmt"
"sync"
"time"
)
// 公司可以让大家借钱(提前支出) 还钱
// 账户 => 500 * 10000
// 每个人都可以借钱 => 还钱
// 解决方法: 借钱和还钱过程中不要终端
// 加锁
// 共享数据
var money int = 500 * 10000
var locker sync.Mutex
func borrow(m int) int {
locker.Lock()
defer locker.Unlock()
if money < m {
// 释放锁
// locker.Unlock()
return 0
}
money -= m
// locker.Unlock()
return m
}
// 还钱
func repay(m int) {
locker.Lock()
money += m
locker.Unlock()
}
func main() {
wg := sync.WaitGroup{}
for person := 'A'; person <= 'Z'; person++ {
wg.Add(1)
go func(person rune) {
m := borrow(200)
time.Sleep(2 * time.Millisecond)
repay(m)
wg.Done()
}(person)
}
wg.Wait()
fmt.Println("money:", money) // 500 * 10000
}
// 解决问题
// write(context) => file
// 两个人同时打开文件 => 写数据 => 文件会乱 => 加锁 os
// 数据库 =>
互斥锁
Go语言中sync包中提供了Mutex(互斥锁),可以用于对资源加锁和释放锁提供对资源同步方式访问
原子操作
原子操作是指过程不能中断的操作s,go语言sync/atomic包中提供提供了五类原子操作函数,其操作对象为整数型或整数指针
a) Add*:增加/减少s
b) Load*:载入
c) Store*:存储
d) Swap*:更新
e) CompareAndSwap*:比较第一个参数引用指是否与第二个参数值相同,若相同则将第一个参数值更新为第三个参数
管道
数据处理者处理完数据后将数据放入缓冲区中,数据接收者从缓冲区中获取数据,处理者不
用等待接收者是否准备好处理数据
在go语言中可以通过chan来定义管道,可以通过操作符<-和->对管道进行读取和写入操作
通过管道维护例程状态:
- runtime 包
runtime 包提供了与 Go 运行时系统交互的操作,常用函数:
⚫ runtime.Gosched(): 当前 goroutine 让出时间片
⚫ runtime.GOROOT(): 获取 Go 安装路径
⚫ runtime.NumCPU(): 获取可使用的逻辑 CPU 数量
⚫ runtime.GOMAXPROCS(1):设置当前进程可使用的逻辑 CPU 数量
⚫ runtime.NumGoroutine(): 获取当前进程中 goroutine 的数量
常用包
线程安全
package main
import "fmt"
func main() {
user := map[string]string{}
fmt.Println(user)
//并非线程安全
}
练习
作业