美文网首页
Golang下的并发基础

Golang下的并发基础

作者: 9c46ece5b7bd | 来源:发表于2018-04-27 21:32 被阅读108次

    并发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 1 header 2
    ABC aac
    abc

    相关文章

      网友评论

          本文标题:Golang下的并发基础

          本文链接:https://www.haomeiwen.com/subject/ounilftx.html