这是我参与「第五届青训营」笔记创作活动的的第2天
并发 or 并行
并发是多线程程序在单核心的cpu上运行,并行是多线程程序在多核心的cpu上运行。但是,并发和并行并不相同,并发主要是由切换时间片来实现多个线程程序同时运行,而并行则是直接利用多核实现多线程程序的运行,并且Go程序可以设置使用的核心数,以发挥多核计算机的最大能力。
进程 、线程 or 协程
- 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位;
- 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位;
- 协程是独立的栈空间,共享堆空间,调度由用户自己控制,本质上类似于用户级线程;
Goroutine
1. 什么是Goroutine
Goroutine是一个轻量级的可独立运行的工作单元,可以看作是轻量级的线程。与线程相比,创建goroutine的成本很小,因此go应用程序通常会同时运行数千个goroutine。
2. Goroutine的优势
- 创建goroutine的成本远小于线程。Goroutine的栈大小只有几KB,且可以根据应用程序的需要增长和收缩。而对于线程,站的大小必须制定和固定下来。
- 多个goroutine可以复用一个操作系统的线程。在一个有数千个goroutine的程序中可能只有一个线程。如果一个线程是由于goroutine等待用户输入而阻塞,则会创建一个新的线程,其余的goroutine也会移动到这个新的线程运行。这些对于程序员来说都是透明的。
- Goroutine使用通道来通信。通道的使用可以防止访问共享内存时出现资源竞争的问题。
Go语言并发打印(使用通道实现)
package main
import (
"fmt"
)
func printer(c chan int) {
// 开始无限循环等待数据
for {
// 从channel中获取一个数据
data := <-c
// 将0视为数据结束
if data == 0 {
break
}
// 打印数据
fmt.Println(data)
}
// 通知main已经结束循环(我搞定了!)
c <- 0
}
func main() {
// 创建一个channel
c := make(chan int)
// 并发执行printer, 传入channel
go printer(c)
for i := 1; i <= 10; i++ {
// 将数据通过channel投送给printer
c <- i
}
// 通知并发的printer结束循环(没数据啦!)
c <- 0
// 等待printer结束(搞定喊我!)
<-c
}
本例的设计模式就是典型的生产者和消费者。整个例子使用了2个goroutine,一个是main函数,一个是printer函数创建的goroutine。两个goroutine通过通道进行通信(这个通道有数据传送和控制指令两项重要功能)。