Go语言实现异步

6,439 阅读3分钟

同步与异步

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

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

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

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

(以上摘自知乎)

在go语言中使用goroutine其实很容易区分是否为异步,当调用方的 goroutine 和方法执行的 goroutine 不是同一个 goroutine,那么就可以说是异步的。如果是同一个则为同步。

阻塞和非阻塞的区别

阻塞和非阻塞的最大区别在于——是否等待结果,而且在非阻塞里面,主动询问结果被动接收结果(技术上也称回调机制)是两种不同的获取结果的方式。另外,我们也可以看到,异步可以是阻塞的也可以是非阻塞的,但是同步一般不会说阻不阻塞,因为等蛋糕的人(你)做蛋糕的人(你) 是同一个人(产出结果的就是执行方)。

选择阻塞还是非阻塞呢?

阻塞的方式处理结果的地方是原本的 goroutine,而不像非阻塞的回调函数,执行在另外一个 goroutine 上,这一点让非阻塞的方式看起来更加地异步,但是更加异步就意味着更少控制,因为我们的主流程始终是原本的 goroutine,所以非阻塞的方式会让结果的处理逻辑看起来不那么重要,而阻塞的方式由于结果还是交给主流程的 goroutine 去处理,所以会显得更重要。

换句话说,阻塞之所以是阻塞,不就是因为我们需要拿到结果所以才等待的吗?只不过在等待结果产出的时候我们希望可以先去做别的事,不用傻傻地等着,而非阻塞相当于把结果都交给了别人处理,这种方式对结果的重视程度可想而知。于是,我们得出了一个结论:结果重要的时候,选择阻塞的方式;结果不太重要的时候,选择非阻塞的方式

go实现阻塞异步

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
}

go语言实现非阻塞异步

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)
   }()
}