这是我参与「第五届青训营」笔记创作活动的第11天
语言进阶
并发 并行
Go可以充分发挥多核优势,高效运行。从多线程程序运行的角度看,并发是多线程在一个核的CPU上运行,其主要通过时间片的切换来实现同时运行的状态。广义上的并发,可以理解为系统对外的一种特征或者能力。并行则是直接利用多核实现多线程的同时运行,多线程程序在多个核的CPU上运行。并行可以理解为实现并发的一种手段。Go语言实现了并发性能极高的调动模型,然后通过高效的调度,可以最大限度地利用资源,充分发挥多核计算机的优势。可以理解为,Go语言就是为并发而生的。
Goroutine
Go语言的并发模型是基于协程(goroutine)的。协程是轻量级的线程,由Go语言运行时(runtime)管理,它可以在一个线程中并发执行多个协程,而不像传统的线程模型那样需要创建和管理多个线程。
在Go语言中,通过使用关键字 go,可以创建一个新的协程来并发执行一个函数。例如:
func foo() {
fmt.Println("hello, world")
}
func main() {
go foo()
fmt.Println("main function")
}
在这个例子中,foo() 函数被放在一个协程中执行,main() 函数则继续执行。在协程中执行的代码可以与主程序并行运行,而不会阻塞主程序的执行。
Go语言还提供了基于通道(channel)的并发机制,用于协程之间的通信和同步。通道可以看作是一个线程安全的队列,协程可以通过通道来发送和接收数据。例如:
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
}
在这个例子中,主协程创建了一个通道 ch,然后创建了一个新的协程,这个协程向通道 ch 中发送了一个整数值 1。主协程在通道 ch 中接收到这个整数值,并打印出来。
通过这种基于协程和通道的并发模型,Go语言可以方便地实现高并发的程序。
当一个Go程序启动时,它会自动创建一个主协程(也称为主goroutine)。主协程执行main()函数,然后可以创建其他协程来并发执行其他函数。
Go语言的协程采用了M:N线程模型,即多个协程(M)可以在多个操作系统线程(N)上执行。这意味着,一个Go程序中的所有协程可以共享同一个线程池,减少了创建和销毁线程的开销,从而提高了程序的性能。
在协程之间通信和同步的主要机制是通道。通道是一种先进先出(FIFO)的数据结构,支持在协程之间传递数据。通道既可以用于同步,也可以用于异步通信。通道也可以有缓冲,这样在通信时就不需要等待接收方接收数据。
Go语言还提供了一些同步原语,如互斥锁和条件变量,用于保护共享资源的访问。互斥锁和条件变量是阻塞式的,当协程尝试获取锁时,如果锁已被其他协程占用,则会被阻塞,直到锁被释放。
Go语言的并发模型使得编写高效的并发程序变得非常容易。通过协程和通道,我们可以将程序中的不同部分分解为并发执行的独立任务,从而实现高并发和高吞吐量。