Go语言之并发编程|青训营笔记

55 阅读2分钟

这是我参与「第五届青训营」笔记创作活动的的第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通过通道进行通信(这个通道有数据传送和控制指令两项重要功能)。