Go并发 | 青训营笔记

47 阅读3分钟

进程/线程

进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的独立单位

线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

一个进程可以创建和撤销多个线程,同一个进程中的多个进程可以并发执行

并发/并行

多线程程序在单核心的CPU上运行,称为并发;多线程程序在多核心的CPU上运行,go 1.8后默认设置为最大核数,不需自己设置

协程/线程

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

线程:一个线程上可以跑多个协程,协程是轻量级的线程。

Goroutine 介绍

基本概念

goroutine是一种非常轻量级的实现,可在单个进程里执行执行成千上万的并发任务,它是Go语言并发设计的核心。

说到底 goroutine 其实就是线程,但是它比线程更小,十几个 goroutine 可能体现在底层就是五六个线程,而且Go语言内部也实现了 goroutine 之间的内存共享。

使用 go 关键字就可以创建 goroutine,将 go 声明放到一个需调用的函数之前,在相同地址空间调用运行这个函数,这样该函数执行时便会作为一个独立的并发线程,这种线程在Go语言中则被称为 goroutine。

注意

主函数也是一个g程,如果想在主函数中使用其他goroutine,一定要让主函数等待其他goroutine,比如锁,管道,和time包中的sleep函数

用法

//go 关键字放在方法调用前新建一个 goroutine 并执行方法体
go GetThingDone(param1, param2);
//新建一个匿名方法并执行
go func(param1, param2) {
}(val1, val2)
//直接新建一个 goroutine 并在 goroutine 中执行代码块
go {
    //do someting...
}

为什么并发

从整个操作系统层面来说,多个进程是可以并发的,那么并发的价值何在?下面我们先看以下几种场景。

  1. 一方面我们需要灵敏响应的图形用户界面,一方面程序还需要执行大量的运算或者 IO 密集操作,而我们需要让界面响应与运算同时执行。
  2. 当我们的 Web 服务器面对大量用户请求时,需要有更多的“Web 服务器工作单元”来分别响应用户。
  3. 我们的事务处于分布式环境上,相同的工作单元在不同的计算机上处理着被分片的数据,计算机的 CPU 从单内核(core)向多内核发展,而我们的程序都是串行的,计算机硬件的能力没有得到发挥。
  4. 我们的程序因为 IO 操作被阻塞,整个程序处于停滞状态,其他 IO 无关的任务无法执行。

从以上几个例子可以看到,串行程序在很多场景下无法满足我们的要求。下面我们归纳了并发程序的几条优点,让大家认识到并发势在必行:

  • 并发能更客观地表现问题模型;
  • 并发可以充分利用 CPU 核心的优势,提高程序的执行效率;
  • 并发能充分利用 CPU 与其他硬件设备固有的异步性。

补充

后面例子中会用到的函数defer ,匿名函数啊,需要大家自己去搜索引擎学习,自学能力是非常重要的

Go并发通信

并发中的资源竞争

原子性

原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。