实践:GO语言线程多并发Goroutine | 青训营

96 阅读2分钟

Go语言并发特性

1.并发介绍(先补充点操作系统的)

进程与线程
A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
C.一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行。
并发和并行
A. 多线程程序在一个核的cpu上运行,就是并发。
B. 多线程程序在多个核的cpu上运行,就是并行。

image.png image.png

协程和线程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。

2. goroutine介绍

goroutine 只是由官方实现的超级"线程池"。 每个实力4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因。

并发不是并行并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行,go可以设置使用核数,以发挥多核计算机的能力。

Go语言中使用goroutine非常简单,只需要在调用函数的时候在前面加上go关键字,就可以为一个函数创建一个goroutine。

一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。

func hello() {
    fmt.Println("Hello ")
}
func main() {
    hello()
    fmt.Println("main ")
}
func main() {
    go hello()
    fmt.Println("main goroutine done!")
}

一次的执行结果只打印了main !,并没有打印Hello Goroutine!

在程序启动时,Go程序就会为main()函数创建一个默认的goroutine。 当main()函数返回的时候该goroutine就结束了,所有在main()函数中启动的goroutine会一同结束

启动多个goroutine

func hello(i int) {
    defer wg.Done() 
    fmt.Println("Hello Goroutine!", i)
}
func main() {

    for i := 0; i < 10; i++ {
        wg.Add(1) /
        go hello(i)
    }
    wg.Wait() 
}

会发现每次打印的数字的顺序都不一致。这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。

3.goroutine与线程

OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这个大。所以在Go语言中一次创建十万左右的goroutine也是可以的。