前言

再写go的时候学到的一些特性和使用方法,在这记录一下,感觉面试什么的会遇到。

go工作池

worker pool,也就是线程池thread pool,不过在go里使用的goroutine而非线程。

goroutine

先理解一下goroutine,我个人并没有仔细研究过go的原理,之前一直写java和python,所以goroutine对我来说是个很新奇的东西。

线程、协程和goroutine

我么都知道go是从语言层面就支持并发。为了更好的理解goroutine,先复习一下线程和协程:

  • 线程
    有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
    线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程的切换一般也由操作系统调度。
  • 协程
    又称微线程与子例程(或者称为函数)一样,协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。
    和线程类似,共享堆,不共享栈,协程的切换一般由程序员在代码中显式控制。它避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂。

线程和协程的区别:

  • 进程是cpu分配的基本单位,线程是独立运行和独立调度的基本单位,协程是基于线程上更轻量化的存在
  • 一个进程可以拥有多个协程
  • 进程是同步机制,协程是异步机制

goroutine本质上是一个协程,不同于协程的是,golang在 runtime、系统调用等多方面对 goroutine 调度进行了封装和处理。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU。
但盲目使用goroutine会使得cpu的负荷过高。

context机制

go的上下文是专指goroutine的上下文,包含goroutine的运行状态、环境、现场等信息
当我们在处理一个比较复杂的协程环境,如一个请求衍生出多个协程,这些协程共用一些变量,或完成相同的工作,那这是想用cl关闭这些协程

go异步

释义

先抄一段同步与异步的解释:
同步异步 , 举个例子来说,一家餐厅吧来了5个客人,同步的意思就是说,来第一个点菜,点了个鱼,好, 厨师去捉鱼杀鱼,过了半小时鱼好了给第一位客人,开始下位一位客人,就这样一个一个来,按顺序来。

相同, 异步呢,异步的意思就是来第一位客人,点什么,点鱼,给它一个牌子,让他去一边等吧,下一位客人接着点菜,点完接着点让厨师做去吧,哪个的菜先好就先端出来。

同步的优点是:同步是按照顺序一个一个来,不会乱掉,更不会出现上面代码没有执行完就执行下面的代码, 缺点:是解析的速度没有异步的快。

异步的优点是:异步是接取一个任务,直接给后台,在接下一个任务,一直一直这样,谁的先读取完先执行谁的, 缺点:没有顺序 ,谁先读取完先执行谁的 ,会出现上面的代码还没出来下面的就已经出来了,会报错。

在go中,异步表现为不同的goroutine开启任务。

阻塞与非阻塞

简单来说,就是是否等待结果。等待结果就是阻塞,不等待结果就是非阻塞。

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

正常来说,阻塞更符合我们的编程思维,也就是结果重要。
简单贴一个代码:

package main

import "fmt"

func main() {

   // 方法的两个参数
   a := 1
   b := 2

   // 从管道中接收结果,这一步是阻塞的,因为在等待结果的产出
   sum := <-addAsync(a, b)
   fmt.Println(sum)
}

func addAsync(a int, b int) chan int {
   // 使用管道接收结果,注意需要设置一个缓冲位,否则没有取结果的话这个 goroutine 会被阻塞
   resultChan := make(chan int, 1)
   go func() {
      // 在新的 goroutine 中计算结果,并将结果发送到管道
      resultChan <- a + b
   }()
   return resultChan
}

非阻塞代码:

package main

import (
"fmt"
"time"
)

func main() {

   // 方法的两个参数
   a := 1
   b := 2

   // 调用方法的时候加上回调函数
   // 这个回调函数会在得到结果之后执行
   addWithCallback(a, b, func(sum int) {
      fmt.Println(sum)
   })

   // 防止 main goroutine 比异步任务的 goroutine 先退出
   time.Sleep(time.Second)
}

func addWithCallback(a int, b int, callback func(sum int)) {
   go func() {
      // 在新的 goroutine 中计算结果,并将结果传递给回调函数
      sum := a + b
      callback(sum)
   }()
}

"孓然一身 , 了无牵挂"