【青训营day2】go语言高并发场景的本质 | 青训营笔记

60 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

并发与并行

对于GO语言来说,充分发挥了多核优势,实现了高效运行

并发

并发指的是多线程程序在一个核的CPU上运行,通过时间片的切换来实现多个任务同时运行的状态。

并行

并行指的是多线程程序在多个核的CPU上运行

协程与线程

协程

用户态,轻量级现成,栈MB级别

线程

内核态,线程跑多个协程,栈KB级别

Goroutine

快速打印hello goroutine:0~4

func hello(i int) {
    println("hello goroutine : "+ fmt.Sprint(i))
}
func HelloGoRoutine() {
    for i:= 0;i<5;i+ {
    go func(j int) {
        hello(j)
}(i)
time.Sleep(time.Second)

这段代码能够实现乱序输出 调用函数时加上go为函数调用协程运行函数

CSP(Communicating Sequential Processes)

go语言提倡通过通信来共享内存,而并非通过共享内存来实现通信 通过channel来实现不同协程之间数据的通信

Channel

创建方式: make(chan 元素类型,[缓冲大小]) 分为有缓冲通道和无缓冲通道 。

无缓冲通道

使用无缓冲通道时,创建channel对象时无需声明缓冲大小

使用无缓冲通道会导致收发的协程处于同步状态,所以也被称为同步通道。

有缓冲通道

生产-消费模型

当缓冲区中数据存放满后会产生阻塞,无法继续向其中继续发送数据。

例程

package concurrence  
  
func CalSquare() {  
    src := make(chan int)  
    dest := make(chan int, 3)  
    go func() {  
        defer close(src)  
        for i := 0; i < 10; i++ {  
            src <- i  
        }  
    }()  
    go func() {  
        defer close(dest)  
        for i := range src {  
            dest <- i * i  
        }  
    }()  
    for i := range dest {  
        //复杂操作  
        println(i)  
    }  
}

例程效果

A子协程发送发数字0~9

B子协程计算输入数字的平方

主协程输出最后的平方数

实现思路

由于生产者的生产速度大于消费者的消费速度,此时使用带缓冲的通道

A协程生成0~9后输出到同步通道src中

B协程从src中取出数字,计算平方数后输出到异步通道dest中。

最后主协程遍历dest,并将其输出

并发安全 Lock

对变量执行2000次+1操作,5个协程并发执行。

在加锁的情况下,变量输出为10000,符合预期值

在不加锁的情况下,变量输出为随机值,这种不加锁的行为被称为undefine

WaitGroup

等待协程同步,可以使用waitgroup,而并非sleep。

开启协程时使用Add(n)

关闭协程使用Done()

等待计数器为0使用Wait()