首页 > 代码库 > GO_11:GO语言基础之并发concurrency

GO_11:GO语言基础之并发concurrency

并发Concurrency

  很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级“线程池”而已。不过话说回来,每个实例 4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。另外,goroutine 的简单易用,也在语言层面上给予了开发者巨大的遍历。

  高并发当中一定要注意:并发可不是并行。

  并发主要由切换时间片来实现“同时”运行,而并行则是直接利用多核实现多线程的运行,但 Go 可以设置使用核数,以发挥多核计算机的处理能力。

  goroutine 奉行通过通信来共享内存,而不是共享内存来通信。Go 语言主要是通过 Channe 技术通信来实现内存的共享的,因为 channel 是一个通道,Go 是通过通道来通信进行内存数据的共享。

让我们先来看一个最简单的 goroutine 案例:

package main

import (
    "fmt"
    "time"
)

func main() {
    //启用一个goroutine
    go GoRun()
    //这里加一个休眠是因为主线程已启动就执行完毕消亡来,子线程还来不及执行
    time.Sleep(2 * time.Second)
}

func GoRun() {
    fmt.Println("Go Go Go!!!")
}

运行结果:

Go Go Go!!!

Channel

1. Channel 是 goroutine 沟通的桥梁,大都是阻塞同步的

2. 它是通过 make 创建,close 关闭

3. Channel 是引用类型

4. 可以使用 for range 来迭代,不断操作 channel

5. 可以设置单向 或 双向通道

6. 可以设置缓存大小,在未被填满前不会发生阻塞,即它是异步的

那么针对上溯代码我们不使用休眠,而使用 Channel 来实现我们想要的效果:

package main

import (
    "fmt"
)

func main() {
    //声明创建一个通道,存储类型为bool型
    c := make(chan bool)
    //启用一个goroutine,使用的是匿名方法方式
    go func() {
        fmt.Println("Go Go Go!!!")
        c <- true  //向 channel 中存入一个值
    }()
    //当程序执行完毕之后再从通道中取出刚才赋的值
    <- c
    /**
    主线程启动了一个匿名子线程后就执行到了:<-c , 到达这里主线程就被阻塞了。只有当子线程向通道放入值后主线程阻塞才会被释放
    其实这个就是完成了消息的发送
     */
}

上溯代码可以修改为使用 for range 来进行消息的发送:

package main

import (
    "fmt"
)

func main() {
    //声明创建一个通道,存储类型为bool型,这里设置的channel就是双向通道,既可以存也可以取
    c := make(chan bool)
    //启用一个goroutine,使用的是匿名方法方式
    go func() {
        fmt.Println("Go Go Go!!!")
        c <- true  //向 channel 中存入一个值
        close(c)  //切记如果使用for range来进行取值的时候需要在某个地方进行关闭,否则会发生死锁
    }()
    //从通道中循环取出刚才赋的值
    for v := range c {
        fmt.Println(v)
    }
}

  从以上代码可以看出,一般使用的 Channel 都是双向通道的,即:既可以取又可以存。那单向通道一般用于什么场景下呢?

  单向通道又分为两种,一种是只能读取,一种是只能存放,一般用于参数类型传递使用。例如有个方法返回一个Channel类型,一般要求操作只能从这里取,那么此时它的用途就是只能存放类型,如果此时你不小心存数据,此时会发生panic 导致程序奔溃发生异常。那么读取类型的Channel同理。这样做其实也是为了程序的安全性与健壮性,防止一些误操作。

  这里还有一个知识点,就是有缓存的channel 和 无缓存的channel的区别?

  make(chan bool, 1) 表示带有一个缓存大小的缓存channel;make(chan bool) 表示一个无缓存的channel

  无缓存channel是阻塞的即同步的,而有缓存channel是异步的。

  怎么说?比如

  c1:=make(chan int)         无缓冲

  c2:=make(chan int,1)      有缓冲

  c1<-1                            

  无缓冲的 不仅仅是 向 c1 通道放 1 而是 一直要有别的携程 <-c1 接手了 这个参数,那么 c1<-1 才会继续下去,要不然就一直阻塞着

  而 c2<-1 则不会阻塞,因为缓冲大小是1 只有当 放第二个值的时候 第一个还没被人拿走,这时候才会阻塞。

  打个比喻

  无缓冲的  就是一个送信人去你家门口送信 ,你不在家 他不走,你一定要接下信,他才会走。

  无缓冲保证信能到你手上

  有缓冲的 就是一个送信人去你家仍到你家的信箱 转身就走 ,除非你的信箱满了 他必须等信箱空下来。

  有缓冲的 保证 信能进你家的邮箱

 

GO_11:GO语言基础之并发concurrency