Golang下的并发基础

并发concurrency

goroutine只是由官方实现的超级”线程池”,每个实例4-5kb的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是造成golang高并发的根本原因。

并发并不是并行: concurrency is not parallelism

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

Goroutine奉行通过通信来共享内存,而不是共享内存来通信。

Channel

  • channel是goroutine沟通的桥梁,大部分是阻塞同步的
  • 通过make创建,close关闭
func main() {
    //创建一个channel
    c := make(chan bool)
    //使用goroutine操作匿名函数
    go func() {
        fmt.Println("go biaoge!")
        //想channel中存值
        c <- true
    }()
    //从channel c中取值
    <- c
    }
  • channel是引用类型
  • 可以使用for range来结合close函数来操作channel
func main() {
    c := make(chan bool)
    go func() {
        fmt.Println("go biaoge!")
        c <- true
        //每次完成后进行关闭channel;否则的话会变成死锁;所以在使用channel的时候,一定需要再一个地方显式的关闭channel
        close(c)
    }()
    //<- c
    //使用range来获取channel中的值
    for v := range c{
        fmt.Println(v)
        }
}
  • 可以设置单向或双向通道
    双向通道一般是既能存值,又能取值 c := make(chan bool)

  • 可以设置缓存大小,在未被填满前不会发生阻塞
    缓存为0的话就是阻塞的

没有缓存的channel,取的操作先于放的操作

    // 无缓存的channel
    a := make(chan bool)
    go func() {
        fmt.Println("go biaoge!")
        <- a
        close(c)
    }()
    a <- true
    //

有缓存的channel:放的操作先与取的操作

    //有缓存的channel
    e := make(chan bool,1)
    go func() {
        fmt.Println("1231")
        e <- true
    }()
    <- e

总结:

  • 有缓存的是异步的
  • 无缓存的是同步阻塞的

保证并发过程中能够执行完成所有的任务,第一种方式是使用有缓存的channel,第二种方式是使用sync

package main
import (
    "fmt"
    "runtime"
)

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    c := make(chan bool,10)
    for i :=0;i < 10;i++ {
        go Go(c,i)
        //<- c
    }
    //通过有缓冲的channel来实现多核心的高并发
    for i := 0;i < 10;i++ { <- c }
}

func Go(c chan bool,index int) {
    a := 1
    for i :=0 ;i < 1000000;i++ {
        a += i

    }
    fmt.Println(index,a)
    c <- true
}

package main
import (
    "fmt"
    "runtime"
    "sync"
)

//使用sync包中的WaitGroup{}来实现任务的等待
func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    wg := sync.WaitGroup{}
    wg.Add(10)
    for i :=0;i < 10;i++ {
        go Go(&wg,i)
    }

    wg.Wait()
}


func Go(wg *sync.WaitGroup,index int) {
    a := 1
    for i :=0 ;i < 1000000;i++ {
        a += i

    }
    fmt.Println(index,a)
    //好像是每Done()一次,就会在wg中减一次
    wg.Done()

}

Select

  • 可以处理一个或多个channel的发送和接收
package main
import (
    "fmt"
)


func main() {
    c1,c2 := make(chan int),make(chan string)

    //因为是使用goroutine的,因此无法确定c1和c2的存取顺序,如果最后那个c2优先执行了,那么整个程序也会因为main函数的结束而关闭。因此这个时候需要再创建一个channel来判断整个goroutine中的任务十分完成

    done := make(chan bool)
    go func() {
        for {
            select {
                case v,ok := <- c1:
                    if !ok {
                        //如果c1 或者c2没有取成功,则表示任务执行完成,往done的通道里存值
                        done <- true
                        break

                    }
                    fmt.Println("c1",v)
                case v,ok := <- c2:
                    if !ok {
                        done <- true
                        break
                    }
                    fmt.Println("c2",v)
            }
        }
    }()


    c1 <- 1
    c2 <- "test-biaoge"
    c1 <- 100
    c2 <- "bgops"

    //一定要显式关闭channel
    close(c1)
    close(c2)

    //取出done通道里的内容来判断任务的执行完成
    <- done
}

需要注意的是,如果channel是有零值的,可能在误关闭channel的时候导致不断读取零值

使用select进行存取

package main
import (
    "fmt"
)

func main() {
    c := make(chan int)
    go func() {
        for v:=range c{
            fmt.Println(v)
        }
    }()

    for i :=0;i < 10 ;i++ {
        //使用select对channel进行写入操作
        select {
            case c <- 0:
            case c <- 1:
        }
    }
}
  • 同时又多个可用的channel时按随机顺序处理
  • 可用空的select来阻塞main函数(事件循环)
  • 可设置超时
package main
import (
    "fmt"
    "time"
)

func main() {
    c := make(chan bool)
    select {
        //去c里面读取值
        case v := <- c:
            fmt.Println(v)
        //读取超时
        //time.After()返回的也是一个chan
        case <- time.After(3*time.Second):
            fmt.Println("Channel Timeout")
    }

}
header 1header 2
ABCaac
abc
    原文作者:BGbiao
    原文地址: https://www.jianshu.com/p/b677bc9ff964
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞