并行与并发
- 并发(Concurrency)是指在单个处理器上同时执行多个任务的能力。它是通过在时间上交替执行任务,使得每个任务都有机会向前推进。在并发中,任务之间相互独立,每个任务按照自己的节奏执行,但它们之间会交替执行。使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的。
- 并行(Parallelism)指在同一时刻,有多条指令在多个处理器上同时执行。
- 并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头。
- 并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发是并行的假象,并行可以认为实现并发的手段。
计算机的"大脑"。
- 在计算机领域中,处理器和CPU(Central Processing Unit)通常可以互换使用。它们都指代计算机的中央处理器部件,负责执行指令和进行计算。
- 处理器可以是单核或多核的,分别支持并发执行和并行处理。
- 每个核心都是一个独立的处理单元,每个核心都包含算术逻辑单元(ALU)、寄存器、高速缓存等,它可以执行指令和进行数据处理。
进程、线程、协程
-
进程(Process):进程是操作系统分配资源和管理程序执行的基本单位。进程之间是相互独立的,拥有独立的内存空间,可以同时运行多个进程,实现多任务操作。进程间通信需要使用额外的机制,如共享内存、管道等。
-
线程(Thread):线程是进程中执行任务的最小单位。程共享进程的内存空间和上下文,可以同时执行多个线程,实现多线程并发操作。线程之间可以直接通信和共享数据。
-
协程(Coroutine):协程是一种用户态的轻量级线程,它并不是由操作系统内核来管理和调度,而是由程序员控制其执行和切换的时机。可以在同一个线程内实现多个协程的切换,实现并发和并行操作。
- 进程是程序执行的过程,包括了动态创建、调度和消亡的整个过程。 进程是一个正在运行的程序,而线程是进程中的一个执行单元。
- 一个核心可以同时处理一个进程或一个线程。
- 当一个核心处理一个进程时,它会在不同进程之间切换,每个进程获得一小段时间来执行。当一个核心处理一个线程时,它会在不同线程之间切换,以实现并发执行。 多核处理器可以同时处理多个进程和多个线程,以提高计算机的处理能力和并行处理能力。
- 执行线程是在进程内部进行的,但并不是一定需要一个进程存在在一些嵌入式系统或特定的环境下,可以直接创建和执行线程,而不需要一个完整的进程。这种情况下,线程可以更轻量级地创建和销毁,因为它们不需要独立的进程资源。 然而,在大多数的操作系统和计算机系统中,线程是依附于进程的。也就是说,当一个进程创建时,至少会有一个线程存在,这个线程通常被称为主线程。进程创建后,可以动态地创建更多的线程,并在这些线程之间进行切换和执行。
Go并发
Go中的多线程编程称为goroutine,目前统一译为协程。因为协程kb级别,轻量很多,一次可以创建上万个协程,这也就是Go语言更适合高并发场景的原因。- go 的并发编程采用的 CSP (Communicating Sequential Process) 模型,主要基于协程 goroutine 和通道 channel。
- 在函数中调用了休眠(
sleep)函数,这个函数就会告诉Go调度器去调度其他可被调度的Go协程。- 调度器(Scheduler)是操作系统或者编程语言运行时系统的一部分,负责协调和管理多个任务或线程的执行顺序和分配计算资源。
- 在
Go语言中,调度器(Goroutine Scheduler)是运行时系统的一部分,负责调度和管理Goroutine的执行。它使用了一种称为“抢占式的协作式调度(Preemptive Cooperative Scheduling)”的方式,即允许Goroutine主动让出执行权,也可以被强制抢占执行权,以确保公平性和避免资源竞争。- 只有非休眠(non-sleeping)的Go协程才会被认为是可被调度的。
Channel
- src 是源代码(Source Code)的缩写,通常用于指代源代码文件或源代码目录。
- go语言提供的消息通信机制称为channel,
go语言在设计上强调不要通过共享内存来通信,而应该通过通信来共享内存,但实际都有存在呢。 - Go语言中的channel有三种基本操作:发送、接收、关闭。
- 关闭一个channel之后,就不能再向它发送数据了,但是仍然可以从它接收数据。
- 阻塞式机制:当一个协程向一个channel发送数据时,如果channel已经满了,发送操作会被阻塞,直到有其他协程从channel中取走了数据。同样地,当一个协程从一个channel中接收数据时,如果channel中没有数据可供接收,接收操作会被阻塞,直到有其他协程向channel中发送了数据。这种阻塞式的机制可以保证协程之间的同步和通信。
- 容量 :无缓冲channel的容量为0,意味着它不能存储任何数据,数据发送者必须等待数据接收者接收数据后才能继续发送。有缓冲channel的容量大于0,可以存储一定数量的数据,只有当缓冲区满时,发送操作才会阻塞。
- 在
Go语言中,通过在函数或方法后面使用一对括号,可以直接调用该函数或方法。
import (
"fmt"
)
func printhello() {
fmt.Println("hello world")
}
func main() { // 声明 main 主函数
fmt.Println("Hello")
go printhello()
fmt.Println("world")
}
func CalSquare() {
src := make(chan int)
dest := make(chan int, 3)
go func() {
defer cLose(src )
for i:=0;i<10;i++{
src <- i
}
}()
go func() {
defer cLose(dest)
for i := range src {
dest <- i * i
}
}()
for i := range dest {
println(i)
}
依赖管理
- 依赖包和SDK类似,都是指向提供特定功能的软件工具或库。
- 对于依赖包的管理经历了
GOPATH,GO VENDOR, GO MODULE(由于对于sdk包版本的兼容控制)
- 依赖分发proxy的模式---缓存的模式 具有相同思想,学下去。