天天看点

Go语言学习笔记-并发

并发与并行的区别:

区别:

1. 并发:☞逻辑上具备同时处理多个任务的能力,指的是多个任务在同一时间段执行而不是同一时刻

2. 并行:☞物理上同一时刻执行多个任务

goroutine:

goroutine像一个多线程和协程的综合体,运行时会创建多个限制来执行并发任务,且任务单元可调度到其他线程并行执行,最大限度提升执行效率。

go关键字

go println("hello world")

go funct(s){
    println(s)
}("hello world")
           

创建并发任务通过go关键字就可,但是关键字go并非执行并发操作,而是创建一个并发任务单元,放置于系统队列中等待调度器调度执行。等待执行过程中,当前流程不会阻塞,也不会等待该任务启动,存在多个任务时也不会去保证并发任务的执行次序。

每个任务单元相比系统线程栈的MB级别来说小了很多,goroutine定义栈初始只需要2KB。自定义栈采用按需分配的策略,在需要时进行扩容,最大能达到GB规模。

go关键字与defer一样,会因为”延迟执行“而立即计算并复制执行参数,可参考下面例子:

package main

import "time"

var c int

func counter() int {
    c++
    return c
}

func main() {
    a :=
    go func(x, y int) {
        time.Sleep(time.Second)
        println("go:", x, y)
    }(a, counter())

    a +=
    println("main:", a, counter())
    time.Sleep(time.Second *)
}

//输出结果:
main:
go:
           

通道

Go内置CSP模型,且鼓励使用CSP通道,以通信来代替内存共享,实现并发安全,不需要开发人员自行维护数据一致和完整性。

channel是显式的,收发双方必须都知道数据类型和具体通道。

channel从底层实现上来说是一个队列,同步模式下收发双方必须配对,然后直接复制数据给对方,配对失败则置入等待队列,直到出现适配的另一方

异步模式抢夺的是数据缓冲槽,发送方要求有空草可供写入,接收方要求有缓冲数据可读。

通道除了传递数据之外,通常也被用作事件通知。

func main(){
  done := make(chan struct{}) //结束事件
  c := make(chan string)     //数据传输通道

  go func(){
    s:=<-c        //接受数据
    println(s)
    close(done)   //关闭通道,作为结束事件通知
  }()
  c <- "hi!"    //发送数据
  <-done        //阻塞,置入等待 等待数据或事件通知
}
           

异步模式

可以使用len以及cap函数来获取缓冲区的大小以及缓冲区已缓冲的数量

对于同步模式的channel使用len以及cap返回结果均为0,此不同可用作区分channel是同步还是异步

import "unsafe"
func main(){
  c := make(chan int)       //创建一个带3个缓冲槽的异步通道
  c
  c
  println("通道中第一个值:    ",<-c)   //取出第一个数据
  println("通道中第二个值:    ",<-c)   //取出第二个数据
  println("通道中写入值的数量:    ",len(c))
  println("通道的容积:    ",cap(c))
  println("通道变量的地址:    ",c,"通道的长度:    ",unsafe.Sizeof(c))
}

//输出
//通道中第一个值:     1
//通道中第二个值:     2
//通道中写入值的数量:     0
//通道的容积:     3
//通道变量的地址:     0xc42006a000 通道的长度:     8