开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
对于并发知识的一些相关总结,发现自己对于进程、线程、协程只能够说出他们的概念,对于他们特性和不同还不是很了解,面试的时候就惨遭拷打了,所以做笔记总结一下。
进程
进程是程序在操作系统的一次执行过程,是操作系统进行资源分配的调度的一个最小单位(程序执行的最小单元)。是程序运行的载体。
线程
线程是进程执行中的一个实体,是CPU调度和分派的基本单位(操作系统分配的最小单元),是比进程更小的能独立运行的基本单位。
进程和线程的比较
-
进程是程序执行的基本单元,而线程是操作系统分配资源的最小单元。
-
一个进程中包含一个或者多个线程,同一进程中的线程共享程序的内存空间并且可以并发执行。进程之间相互独立,进程中的线程对其他进程不可见。
-
线程比进程的上下文切换要快得多。
协程:
相比线程更加轻量,拥有独立的栈空间和栈内存,调度由用户自己控制,相当于轻量级的线程。类似于用户级别的线程,调度也是由用户实现。
协程和线程的比较
- 协程的栈空间比线程更小,线程默认是1M,而协程是1K,更加轻量级,因此在相同内存下可以开启更多的协程。
- 协程的切换在用户态,而线程的切换在内核态,因此减少了上下文消耗,提高了效率。
- 只有一个线程,不会出现锁竞争的情况造成阻塞,共享资源只需要判断状态就好了。但是不适用于需要大量计算的多线程,适用于被阻塞、需要大量并发的场景。
goroutine:
golang中的并发设计的核心,也叫协程。它不需要用户关心底层逻辑内存分配、垃圾回收等,只需要专注于业务开发。每个4~5KB的栈空间内存占用,和由于实现机制而大幅减少的创造、销毁开销是Go高并发的核心。
并发
多线程程序在一个核的cpu上运行,就是并发。CPU根据时间片切分,交替执行多个程序。但是这个切换的过程我们用户是感知不到的。

并行
多线程程序在多个核的cpu上运行,就是并行。多核CPU在同时执行多个程序
并发和并行的区别:
并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行,go可以设置使用核数,以发挥多核计算机的能力。
- go语言中最小资源单位为go程---->goroutine,go语言原生支持并发,就是靠goroutine实现的。
- 只需要在函数前增加go关键字
- 在go中通过通信来共享内存,而不是通过共享内存来通信
- 通过创建计数器WaitGroup,来保证主进程不会提前退出
//创建一个计数器WaitGroup,
var wg sync.WaitGroup
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) //每启动一个go routine计数器+1
go hello(i)
}
fmt.Println("主函数输出")
wg.Wait() //等待所有计数器为0时才退出
}
func hello(i int) {
defer wg.Done() //goroutine运行结束后计数器-1
fmt.Println("运行go程:", i)
}
管道之间通信:
- 通常通过for range来遍历管道内的数据,并且以此来判断有没有关闭
- 双向通道可以赋值给单向读、写通道,但是反过来不行
- 通道写完后一定要关闭,同时要保证读写次数一致,
var wg1 sync.WaitGroup
func main() {
//声明channel变量如果不用make初始化则为nil
var ch1 chan int // 声明一个传递整型的通道
fmt.Println(ch1) //此时为空,因为没有初始化
//创建无缓冲管道
//numChan := make(chan int)
//创建有缓冲管道
numChan := make(chan int, 10)
wg1.Add(2)
go read(numChan)
go write(numChan)
//input(ch1, numChan)
wg1.Wait()
}
func write(ch chan int) {
defer wg1.Done()
for i := 0; i < 50; i++ {
ch <- i
fmt.Println("往管道中写入数据", i)
}
//数据发送完之后一定要关闭,不然会报错deadlock
close(ch)
}
func read(ch chan int) {
defer wg1.Done()
//使用for range判断管道是否关闭,关闭了的话读取完后就终止循环
for i := range ch {
fmt.Println("读取到数据", i)
}
}
//在参数声明单向读或写channel,可以从双向管道赋值
func input(out chan<- int, in <-chan int) {
}

进程
进程是程序在操作系统的一次执行过程,是操作系统进行资源分配的调度的一个最小单位(程序执行的最小单元)。是程序运行的载体。
线程
线程是进程执行中的一个实体,是CPU调度和分派的基本单位(操作系统分配的最小单元),是比进程更小的能独立运行的基本单位。
进程和线程的比较
-
进程是程序执行的基本单元,而线程是操作系统分配资源的最小单元。
-
一个进程中包含一个或者多个线程,同一进程中的线程共享程序的内存空间并且可以并发执行。进程之间相互独立,进程中的线程对其他进程不可见。
-
线程比进程的上下文切换要快得多。
协程:
相比线程更加轻量,拥有独立的栈空间和栈内存,调度由用户自己控制,相当于轻量级的线程。类似于用户级别的线程,调度也是由用户实现。
协程和线程的比较
- 协程的栈空间比线程更小,线程默认是1M,而协程是1K,更加轻量级,因此在相同内存下可以开启更多的协程。
- 协程的切换在用户态,而线程的切换在内核态,因此减少了上下文消耗,提高了效率。
- 只有一个线程,不会出现锁竞争的情况造成阻塞,共享资源只需要判断状态就好了。但是不适用于需要大量计算的多线程,适用于被阻塞、需要大量并发的场景。
goroutine:
golang中的并发设计的核心,也叫协程。它不需要用户关心底层逻辑内存分配、垃圾回收等,只需要专注于业务开发。每个4~5KB的栈空间内存占用,和由于实现机制而大幅减少的创造、销毁开销是Go高并发的核心。
并发
多线程程序在一个核的cpu上运行,就是并发。CPU根据时间片切分,交替执行多个程序。但是这个切换的过程我们用户是感知不到的。
并行
多线程程序在多个核的cpu上运行,就是并行。多核CPU在同时执行多个程序
并发和并行的区别:
并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行,go可以设置使用核数,以发挥多核计算机的能力。
- go语言中最小资源单位为go程---->goroutine,go语言原生支持并发,就是靠goroutine实现的。
- 只需要在函数前增加go关键字
- 在go中通过通信来共享内存,而不是通过共享内存来通信
- 通过创建计数器WaitGroup,来保证主程不会提前退出
//创建一个计数器WaitGroup,
var wg sync.WaitGroup
func main() {
for i := 0; i < 10; i++ {
wg.Add(1) //每启动一个go routine计数器+1
go hello(i)
}
fmt.Println("主函数输出")
wg.Wait() //等待所有计数器为0时才退出
}
func hello(i int) {
defer wg.Done() //goroutine运行结束后计数器-1
fmt.Println("运行go程:", i)
}
管道之间通信:
- 通常通过for range来遍历管道内的数据,并且以此来判断有没有关闭
- 双向通道可以赋值给单向读、写通道,但是反过来不行
- 通道写完后一定要关闭,同时要保证读写次数一致,
var wg1 sync.WaitGroup
func main() {
//声明channel变量如果不用make初始化则为nil
var ch1 chan int // 声明一个传递整型的通道
fmt.Println(ch1) //此时为空,因为没有初始化
//创建无缓冲管道
//numChan := make(chan int)
//创建有缓冲管道
numChan := make(chan int, 10)
wg1.Add(2)
go read(numChan)
go write(numChan)
//input(ch1, numChan)
wg1.Wait()
}
func write(ch chan int) {
defer wg1.Done()
for i := 0; i < 50; i++ {
ch <- i
fmt.Println("往管道中写入数据", i)
}
//数据发送完之后一定要关闭,不然会报错deadlock
close(ch)
}
func read(ch chan int) {
defer wg1.Done()
//使用for range判断管道是否关闭,关闭了的话读取完后就终止循环
for i := range ch {
fmt.Println("读取到数据", i)
}
}
//在参数声明单向读或写channel,可以从双向管道赋值
func input(out chan<- int, in <-chan int) {
}