vault backup: 2026-06-07 12:14:39
This commit is contained in:
@@ -1,33 +1,91 @@
|
||||
---
|
||||
tags:
|
||||
- Go
|
||||
- golang
|
||||
- go基础语法
|
||||
- 并发概述
|
||||
tags: [go, golang, 并发概述, CSP]
|
||||
create time: 2026-06-07 14:30
|
||||
---
|
||||
|
||||
# 并发概述
|
||||
|
||||
## 进程与线程
|
||||
谈到并发或者并行一个绕不开的话题就是进程和线程,弄清楚进程与线程的本质是并发编程的前提,那么究竟什么是进程,什么是线程呢?
|
||||
可以这样理解:
|
||||
- 进程就是运行着的程序,它是程序在操作系统的一次执行过程,是一个程序的动态概念,进程是操作系统分配资源的基本单位
|
||||
- 线程可以理解为一个进程的执行实体,它是比进程粒度更小的执行单元,也是真正运行在cpu上的执行单元,线程是CPU调度资源的基本单位
|
||||
进程中可以包含多个线程,需要记住进程和线程一个是操作系统分配资源的基本单位(进程),一个是操作系统调度资源的基本单位(线程)
|
||||
## 概述
|
||||
|
||||
## 协程
|
||||
协程可以理解为用户态线程,是更微量级的线程。区别于线程,协程的调度在用户态进行,不需要切换到内核态,所以不由操作系统参与,由用户自己控制。在一些支持协程高级语言中,往往这些语言都实现了自己的协程调度器,比如go语言就有自己的协程调度器,这个会在后面专门讲协程调度原理的时候讲。
|
||||
- 协程有独立的栈空间,但是共享堆空间。
|
||||
- 一个进程上可以跑多个线程,一个线程上可以跑多个协程
|
||||
本文梳理进程、线程、协程的核心概念,区分"并发"与"并行"的本质差异,为后续深入 Go 的并发编程(Goroutine / Channel / Sync)奠定理论基础。
|
||||
|
||||
## 并发与并行
|
||||
很多时候大家对于并行和并发的概念还比较模糊,其实只需要根据一点来判断即可,能不能同时运行。两个任务能同时运行就是并行,不能同时运行,而是每个任务执行一小段,交叉执行,这种模式就是并发。
|
||||
## 正文
|
||||
|
||||

|
||||
### 进程与线程
|
||||
|
||||

|
||||
> [!question] 💭 思考
|
||||
> 一个正在运行的程序,操作系统需要分配哪些资源来支撑它?如果多个程序同时运行,它们之间如何互不干扰?
|
||||
|
||||
如图(并行)所示,两个任务一直运行,切实同时运行着,这就是并行模式,要注意并行的话一定要有多个核的支持,因为只有一个cpu的话,同一时间只能跑一个任务,如图(并发)所示,两个任务,每次只执行一小段,这样交叉的执行,就是并发模式,并发模式在单核cpu上是可以完成的
|
||||
**进程**是操作系统分配资源的基本单位。它是程序在操作系统上的一次执行过程——注意,这里是"执行过程"而非"程序本身",强调的是动态性。
|
||||
|
||||
**线程**是 CPU 调度的基本单位,是进程中实际执行任务的实体。
|
||||
|
||||
| 维度 | 进程 | 线程 |
|
||||
|------|------|------|
|
||||
| 角色 | 资源分配单位 | CPU 调度单位 |
|
||||
| 开销 | 大(独立地址空间) | 小(共享父进程内存) |
|
||||
| 隔离性 | 强(进程间需 IPC) | 弱(共享同一进程内存) |
|
||||
|
||||
> [!warning] ⚠️ 常见误区
|
||||
> "进程 = 重型,线程 = 轻型"——这并不准确。关键区别在于:**进程有独立地址空间,线程共享地址空间**。这也意味着线程间数据共享方便,但更容易出现竞态条件。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
P["进程 Process"] --> T1["线程 Thread 1"]
|
||||
P --> T2["线程 Thread 2"]
|
||||
P --> T3["线程 Thread 3"]
|
||||
style P fill:#e1f5fe
|
||||
```
|
||||
|
||||
一个进程可以包含多个线程,它们共享堆空间和文件描述符,但各自拥有独立的栈空间和寄存器状态。
|
||||
|
||||
### 协程
|
||||
|
||||
> [!question] 💭 思考
|
||||
> 如果线程的创建和切换需要操作系统参与(陷入内核态),有没有一种更轻量的方式来实现并发?
|
||||
|
||||
**协程**是用户态线程,由应用程序自己管理调度,不需要操作系统参与。
|
||||
|
||||
| 对比项 | 线程 | 协程 |
|
||||
|--------|------|------|
|
||||
| 调度位置 | 内核态(OS 调度) | 用户态(语言运行时调度) |
|
||||
| 切换开销 | 高(涉及上下文切换) | 低(仅保存/恢复栈指针) |
|
||||
| 典型实现 | OS pthread / CreateThread | Go goroutine / Python asyncio |
|
||||
|
||||
Go 语言的 Goroutine 就是协程的典型实现——它由 Go 运行时(runtime)在自己的 GMP 调度器下管理,而不是直接映射到 OS 线程。
|
||||
|
||||
> [!tip] 💡 技巧
|
||||
> 一个进程可以有多个线程,一个线程可以有多个协程。这意味着协程比线程更轻量:你可以轻松创建数十万个 goroutine,但开数十万个线程几乎必然耗尽系统资源。
|
||||
|
||||
### 并发与并行
|
||||
|
||||
> [!question] 💭 思考
|
||||
> 单核 CPU 上能实现"同时执行"吗?如果能,那"同时"是什么意思?
|
||||
|
||||
这两个概念经常被混用,区分的关键只有一点:**能不能真正同时执行**。
|
||||
|
||||
- **并行(Parallelism)**:多个任务在同一时刻真正同时在多个核心上执行。必须有多个 CPU 核心支持。
|
||||
- **并发(Concurrency)**:多个任务交替执行,每个任务执行一小段时间片后让出 CPU,宏观上看起来像"同时"在进行。单核 CPU 也能做到。
|
||||
|
||||
```mermaid
|
||||
timeline
|
||||
title 并行 vs 并发
|
||||
section 并行 (多核)
|
||||
Task A : 执行 : 执行 : 执行
|
||||
Task B : 执行 : 执行 : 执行
|
||||
section 并发 (单核)
|
||||
Task A : 执行1 : : 执行3
|
||||
Task B : : 执行2 :
|
||||
```
|
||||
|
||||
如图所示:
|
||||
- **并行**模式下,Task A 和 Task B 在每个时间段都在同时运行——这需要多核 CPU 的支持。
|
||||
- **并发**模式下,两个任务轮流使用 CPU,每次只执行一小段,交叉进行——这在单核 CPU 上即可完成。
|
||||
|
||||
> [!note] 📝 关系总结
|
||||
> 并行一定是并发(因为并行也满足"交替执行"的广义定义),但并发不一定是并行。Go 的并发模型可以在单核上运行,也可以利用多核实现真正的并行——这取决于 `GOMAXPROCS` 的设置。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hzh/GolangStar/Go语言进阶/Goroutine]]
|
||||
- [[hzh/GolangStar/Go语言原理/gmp调度原理]]
|
||||
|
||||
Reference in New Issue
Block a user