并发编程
什么是并发
串行:一条流水线做一件事
并行:多条流水线做多件事
并发:一条流水线做多件事
并行和并发,在编程中,一般称为并发
进程、线程、协程
进程
线程
协程
golang的线程模型
goroutine
程序启动,go为main函数创建一个goroutine
在main函数结束时,main函数里创建的goroutine都会结束
什么是channel
go并发模型,通信共享内存而不是共享内存实现通信。
共享内存,容易引起资源竞争,加锁会影响性能。
内置数据类型,类似队列,先进先出。
通道行为:创建、发送、接收、关闭
基本特性
类型安全:可以传输任何数据
缓冲:
同步
关闭
如何创建channel
go
ch := make(chan int)
// 带缓冲区的
chBuffered := make(chan int, 10)
channel的使用
发送数据到channel
从channel接收数据
不带缓冲channel
没有接收者,会deadLock
带缓冲channel示例
没有接收者,先缓冲,后阻塞
channel的关闭
go
close(ch)
使用range接收channel数据
go
for v := range ch{
fmt.Println(v)
}
channel在并发中的应用
同步
通信
并行处理
示例:并发计算累加和
go
func main(){
var m sync.Mutex
numbers := []int{1,2,3,4,5}
sum := 0
ch := make(chan int)
for _, num := range numbers {
// 每一个数字启动一个goroutine,计算部分和 并发一起发送到channel
// 然后使用range循环接收所有数据,打印最终的累加和
go func(n int){
m.Lock()
sum += n
ch <- sum
m.Unlock()
}(num)
}
var finalSum int
for range numbers {
finalSum <- ch
fmtPrintln("当前总和sum:", finalSum)
}
fmtPrintln("最终总和sum:", finalSum)
}
select
go
func main() {
ch := make(chan int)
go func() {
time.Sleep(3 * time.Second)
ch <- 1
close(ch)
}()
select {
case data, ok := <-ch:
if ok {
fmt.Println("接收到数据: ", data)
} else {
fmt.Println("通道已被关闭")
}
case <-time.After(2 * time.Second):
fmt.Println("超时了!")
}
time.Sleep(3 * time.Second)
}
锁
竞争
go
var x int64
var wg sync.WaitGroup
// wg 好像有点不稳定,万一关了?
func add() {
for i := 0; i < 5000; i++ {
x = x + 1
}
// 线程执行完,需要结束
wg.Done()
}
func main() {
// wg 和 go 的数量有关系
// 多了少了都不行
wg.Add(2)
//wg.Add(3)
go add()
go add()
//go add()
wg.Wait()
fmt.Println(x)
}
加锁
读写锁
总结
go起线程很容易