goroutine与channel学习

一.goroutine
1.1进程、线程、协程

进程(Process):在内存中的程序。有自己独立的独占的虚拟 CPU 、虚拟的 Memory、虚拟的 IO devices。

OS 直接支持并调度。进程之间只能通过系统提供的 IO 机制通讯。共享内存(变量)是不可能的!

  • 每一进程占用独立的地址空间。
    此处的地址空间包括代码、数据及其他资源。
  • 进程间的通信开销较大且受到许多限制。
    对象(或函数)接口、通信协议、…
  • 进程间的切换开销也较大。
    又称Context Switch。
    上下文包括代码、数据、堆栈、处理器状态、资源、…
    注:上下文的定义:每一段程序都有很多外部变量。只有像Add这种简单的函数才是没有外部变量的。一旦你的一段程序有了外部变量,这段程序就不完整,不能独立运行。你为了使他们运行,就要给所有的外部变量一个一个写一些值进去。这些值的集合就叫上下文。

线程(Thread):轻量级进程。在现代操作系统中,是进程中程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。

一个进程由若干线程组成,它们共享进程的计算、存储、IO资源。因此,程序员必须使用系统提供的同步、消息机制,处理资源的竞争和消息的通讯。

  • 多个线程共享进程的地址空间(代码、数据、其他资源等)。
    线程也需要自己的资源,如程序计数器、寄存器组、调用栈等。
  • 线程间的通信开销较少且比较简单。
    因为共享而减少了需要通信的内容。
    但也因为充分共享而无法对共享资源进行保护。
  • 线程间的切换开销也较小。
    只需保存每一线程的程序计数器、寄存器组、堆栈等空间。
    不必切换或复制整个地址空间,从而成本大为降低(约1/10)

线程有分为两大类:

  • 操作系统管理的线程(Core Thread),通常根据 CPU 资源决定线程的数量,一般为 CPU 数量的两倍。
  • 语言提供的线程库管理的线程(User Thread),它执行时映射到系统线程,按任务类型(计算密集型,IO密集型)决定线程池的管理方式与数量。

协程(coroutine/fiber):轻量级线程。 是可以并发执行的函数,由编译或用户指定位置将控制权交给协程调度程序执行的方式。它是非抢占式的,可以避免反复系统调用,还有进程切换造成的开销,给你上几千个逻辑流,也称用户级别线程。

在单线程模式下,协程不需要自己上下文,可以大大减少资源竞争的情况。例如,读写map的项时,不需要锁整个表。在 JavaScript、python等单进程单线程、数据驱动(流式)的应用中,协程比线程更有效率;结合回调函数,更高效的处理 IO 请求。

1.2 goroutine很像线程,但它占用的内存远小于线程,使用它需要的代码更少,goroutine是可以与其他goroutine并行执行的函数,同时也会与主程序并行执行,go语言中会使用同一个线程来执行多个goroutine。在go语言中,net/http库直接使用内置的goroutine,每个接收到的请求都自动在其自己的gorotine里处理,go语言运行时会自动在配置的一组逻辑处理器上调度执行goroutine。每个逻辑处理器绑定到一个操作系统的线程上。

二.channel
channel 是 goroutine 之间通信的一种方式,可以类比成 Unix 中的进程的通信方式管道。可以让用户在不同的goroutine之间同步发送具有类型的消息,这让编程模型更倾向于在goroutine之间发送消息,而不是让多个goroutine争夺同一个数据的使用权。
1.channel创建
channel 使用内置的 make 函数创建,下面声明了一个 chan int 类型的 channel:

ch := make(chan int)

c和 map 类似,make 创建了一个底层数据结构的引用,当赋值或参数传递时,只是拷贝了一个 channel 引用,指向相同的 channel 对象。和其他引用类型一样,channel 的空值为 nil 。使用 == 可以对类型相同的 channel 进行比较,只有指向相同对象或同为 nil 时,才返回 true
channel 的读写操作

ch := make(chan int)

// write to channel
ch <- x

// read from channel
x <- ch

// another way to read
x = <- ch

channel 一定要初始化后才能进行读写操作,否则会永久阻塞。
关闭 channel
golang 提供了内置的 close 函数对 channel 进行关闭操作。

ch := make(chan int)

close(ch)
  • 关闭一个未初始化(nil) 的 channel 会产生 panic
  • 重复关闭同一个 channel 会产生 panic
  • 向一个已关闭的 channel 中发送消息会产生 panic
  • 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已读出,则会读到类型的零值。从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为 false 的 ok-idiom,可以用它来判断 channel 是否关闭
  • 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息

猜你喜欢

转载自blog.csdn.net/weixin_43011810/article/details/84644235