zoukankan      html  css  js  c++  java
  • Go

    并发 vs 并行

    首先,我们先来搞清楚概念以及并发和并行的区别。

    并发 - 利用时间片切换来实现“同时”运行的。

    并行 - 利用CPU的多核来实现通过多线程来实现同时运行。

    Go 语言的设计理念就是通过高并发的方式来进行效率的提升。

    goroutine

    在go语言中,我们就是通过goroutine的方式来处理高并发任务的。

    goroutine 实际上是官方版本的“超级线程池”,每个实例只有4-5KB的栈内存大小,而且它的创建和销毁非常的快速。我们在使用 goroutine 的时候,还可以指定CPU的核数来进一步发挥其高并发处理的优势。

    上面说了很多理论,是时候来写一写code啦。。。第一个goroutine示例

    // concurrency.go
    package main
    
    import (
        "fmt"
    )
    
    func play() {
        fmt.Println("let's go!")
    }
    
    func main() {
        go play() // 使用 "go" 关键字来表示以goroutine的方式调用play方法 
    }

    output:

    从输出中,我们并没有看到所期望的字符串被打印出来,是什么原因呢?? 原来,goroutine的调用即开启了多线程,play方法是在另一个线程中被调用,并且主线程在此刻并没有被阻塞,因此我们的程序直接执行完毕(并没有打印出东东。。。)

    那要如何才能看到输出呢,最简单的办法就是让主线程阻塞一小会,强制等待其他线程先结束。 于是我们可以这样:

    // concurrency.go
    package main
    
    import (
        "fmt"
        "time" //import 'time' package
    )
    
    func play() {
        fmt.Println("let's go!")
    }
    
    func main() {
        go play()
        time.Sleep(3 * time.Second) // force to suspend main thread
    }

    output:

    我们虽然实现了功能,但是code不够优雅,那么怎样才能流畅的在主/从线程之间切换呢??

    Channel

    channel 是为goroutine通信所服务的。用来实现同步或者异步调用。

    使用make关键字来创建channel,完成后使用close关闭channel.

    channel 是引用类型,因此可以直接修改它的字段或状态。

    我们来改写前一个示例:

    // concurrency.go
    package main
    
    import (
        "fmt"
    )
    
    func main() {
        channel := make(chan bool) // define channel
        go func() { // define anonymous function
            fmt.Println("let's go go go...")
            channel <- true  //resume
        }()
        <-channel // suspend
      close(channel)
    }

    output:

     我们来分析一下程序的执行

    1. 首先在主线程(main)里面定义channel,匿名函数。

    2. 使用goruntine的方式调用函数(子线程开始执行,调用方法);主线程继续执行。

    3. 这时候,主线程发现channel读取其中的信号量(没有被设置),此时主线程阻塞。

    4. 子线程调用方法完成,在函数最后更新信号量。

    5. 主线程轮询信号量(已经赋值完成),主线程恢复。

    使用for... range迭代channel(取值)

    我们可以用手动迭代的方式来等待信号量,例如

    func main() {
        channel := make(chan bool)
        go func() { // define anonymous function
            fmt.Println("let's go go go...")
            channel <- false
            close(channel)
        }()
        for v := range channel {
            fmt.Println(v)
        }
        //<-channel
        //close(channel)
    }

    channel 同步与异步

    在使用 channel的时候,我们可以为其设置缓存,通过设置缓存,可以达到同步或者异步的效果。简单来说,当我们不指定缓存的时候,默认值为0,所以在读取的时,如果没读到,就会一直轮询等待。反之,若我们设置了缓存 (>0),再读取的时候会根据缓存忽略轮询,线程则不会阻塞(异步)。

    func main() {
        channel := make(chan bool, 2)
        go func() { // define anonymous function
            fmt.Println("let's go go go...")
            <-channel        
        }()
        
        channel <- false
        close(channel)
    }

    运行上面的code,我们发现不会输出内容。。。原因就是设置了缓存(2),当缓存没有被填满的时候,主线程不再阻塞。

     Select

    当我们需要使用多个channel的时候,就可以通过select来实现。

    func main() {
        c1, c2 := make(chan int), make(chan string)
        c3 := make(chan bool)
        go func() { // define anonymous function
            for {
                select {
                case v, ok := <-c1:
                    if !ok {
                        c3 <- true
                        break
                    }
                    fmt.Println("c1", v)
                case v, ok := <-c2:
                    if !ok {
                        c3 <- true
                        break
                    }
                    fmt.Println("c2", v)
                }
            }
    
        }()
    
        c1 <- 1
        c2 <- "hi"
        close(c1)
        close(c2)
    
        <-c3
    }
    --output: c1 1 c2 hi

    我们还可以使用空select来阻塞主线程

    func main() {
        fmt.Print("the main thread will be suspended...")
        select {} 
    }

    注:这段code只是说明了一下情况,实际当中并不能这样写呀。。。

     

     

  • 相关阅读:
    debian/ubuntu系统vi无法删除字符的解决办法
    kvm磁盘镜像文件管理,格式转换,调整大小
    读懂MACD背离,多空力量分析
    OpenStack部署都有哪些方式
    [STM32F10x] 利用定时器测量频率
    [STM32F10x] 利用定时器测量脉冲宽度
    曼彻斯特编码
    最近关注的几个技术点网页链接
    windows中cmd常用命令收集
    Mybatis Generator代码自动生成(实体类、dao层、映射文件)
  • 原文地址:https://www.cnblogs.com/atuotuo/p/7059531.html
Copyright © 2011-2022 走看看