3

总述

Go依靠独特的轻量级的协程 轻松创建上百万个任务不会导致系统的资源衰竭 (相比起来进程和线程最多也不会超过1w个)

接下来主要从这几个方面展开:

  • 什么是并发编程
  • Go如何实现并发
  • 并发的原理
  • 协程之间通信依靠的chanel
  • 同步异步
  • 数据安全

并发

看起来同时

并行

真正同时

📢注意:

  • 并行一定是多核的
  • 并行看上去很牛逼 但是因为其任务之间的通信成本很高 导致其效果不一定好

进程(process)

每一个独立进行的程序就是进程,是程序中的一次动态执行过程,可与理解为正在执行的程序,是系统资源调度和分配的基本单位,拥有独立的内存单元,需要经历从代码加载,执行,到最后执行完毕。多进程的系统可以同时运行多个程序,因为CPU有自己的分时机制,所以每个程序都可以获取到自己的时间片,但是进程之间的信息交互比较麻烦,另外进程的创建,撤销,切换的开销大。

线程(thread)

线程是一个基本的CPU单元,是CPU调度和分配的最小单位。同一个进程下的线程可以共享系统资源,线程之间的切换代价小于进程之间的切换

协程(goroutine)

是一种用户态的轻量级线程,协程的调度完全通过用户来控制,协程和子函数很类似,一个入口 一次返回 一旦退出 就是完成。


Goroutine

创建的时候 最开始在堆栈上分配一个内存 最开始是4kb 如果不够了再增加 所以go的协程可以同时并发成千上万个goroutine。
我们使用goroutine 的时候是针对某一特定的任务,也可以说是某一段特定的函数or方法,如果一个goroutine里面封装的是main函数,那么我们就称他是主goroutine

如何使用goroutine

直接在要执行的函数前面加一个go关键字,然后执行即可(这种函数往往没有返回值,就算有也会被舍弃).下面这个例子就是一个goroutine打印数字,另一个goroutine打印字母。

package main

import "fmt"

func main() {
    //step1. 先创建一个子goroutinez执行printNum
    go printNum()
    //step2. main中打印字母
    for i := 1; i <= 1000; i++ {
        fmt.Printf("\t主goroutine 打印字母A&%d\n ", i)
    }
    fmt.Println("main func end")
}

func printNum() {
    for i := 1; i <= 1000; i++ {
        fmt.Printf("子goroutine打印数字: %d\n", i)
    }
}

部分运行结果截图如下:

正常情况下,一旦主goroutine执行完毕,程序立即结束,无论子goroutine是否结束,如果想要在所有子goroiutine都结束之后再执行主goroutine就需要用到后面的协程之间通信chanel.

另外,主goroutine还有一点值得注意的是,它会设定每一个goroutine占据空间的最大尺寸, 64位计算机是1GB,32位250M。 如果某个goroutine的栈空间溢出,就会产生一个panic, 随后这个go程序就会终止。 接下来 主goroutine就会进行一系列初始化工作。


LiberHome
409 声望1.1k 粉丝

有问题 欢迎发邮件 📩 liberhome@163.com