协程

作者: 酷走天涯 | 来源:发表于2018-12-24 09:19 被阅读3次

    1.并发是什么?

    并发是指<立即>处理多个任务的能力。

    比如你的双手可以同时做两件事,比如吃饭这件事就是并发,吃饭这个过程中,可以同时吃几种菜,甚至喝汤,这个过程就是一个多任务并发的过程,但是并发在时间上是不能同时进行的

    2.并发和并行的却别

    并行是指<同时>处理多个任务。这听起来和并发差不多,但其实完全不同。

    一边吃饭一边听音乐,听音乐和吃饭可以在时间上同时进行

    3.并行不一定有并发效率高

    并行不一定会加快运行速度,因为并行运行的组件之间可能需要相互通信。在我们浏览器的例子里,当文件下载完成后,应当对用户进行提醒,比如弹出一个窗口。于是,在负责下载的组件和负责渲染用户界面的组件之间,就产生了通信。在并发系统上,这种通信开销很小。但在多核的并行系统上,组件间的通信开销就很高了。所以,并行不一定会加快运行速度!

    4.Go 协程是什么?

    Go 协程是与其他函数或方法一起并发运行的函数或方法。Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。因此在 Go 应用中,常常会看到有数以千计的 Go 协程并发地运行。

    5.go协程 和 线程的优势对比

    相比线程而言,Go 协程的成本极低。堆栈大小只有若干 kb,并且可以根据应用的需求进行增减。而线程必须指定堆栈的大小,其堆栈是固定不变的。
    Go 协程会复用(Multiplex)数量更少的 OS 线程。即使程序有数以千计的 Go 协程,也可能只有一个线程。如果该线程中的某一 Go 协程发生了阻塞(比如说等待用户输入),那么系统会再创建一个 OS 线程,并把其余 Go 协程都移动到这个新的 OS 线程。所有这一切都在运行时进行,作为程序员,我们没有直接面临这些复杂的细节,而是有一个简洁的 API 来处理并发。
    Go 协程使用信道(Channel)来进行通信。信道用于防止多个协程访问共享内存时发生竞态条件(Race Condition)。信道可以看作是 Go 协程之间通信的管道。我们会在下一教程详细讨论信道。

    6.如何启动一个协程

    调用函数或者方法时,在前面加上关键字 go,可以让一个新的 Go 协程并发地运行

    package main
    
    import (
        "fmt"
    )
    
    func hello() {
        fmt.Println("Hello world goroutine")
    }
    func main() {
        go hello()
        fmt.Println("main function")
    }
    

    下面再看一个例子

    package main
    
    import "fmt"
    
    func print(i int){
        fmt.Println(i)
    }
    
    func main() {
    
     for  i := 1 ;i < 40 ;i++{
        go print(i)
    
     }
    
    }
    
    image.png

    我们研究一下这个现象

    
    import "fmt"
    
    func print(i int){
        fmt.Println(i)
    }
    
    func main() {
    
     for  i := 1 ;i < 40 ;i++{
         go print(i)
         fmt.Printf("第 %d 循环",i)
     }
    
    }
    
    image.png

    我们根据这个结果能得出三个结论?

    • 1.go print() 是并发执行了 因为输入的结构是无序的
    • 2.for 循环执行完毕的时候 go print() 没有执行完成
    • 3.当for 循环执行完毕的时候,如果 还有 go print() 的函数没有执行,那么系统就会回收资源,因为go 的协程的作用域是for的子块,for执行完毕后,它占用的内存就会被释放,那么它的子块作用域也会被释放

    go 协程的理论如下

    • 启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。
    • 如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行

    注意 main 函数其实调用也是一个协程,它被称为 主协程

    package main
    
    import "fmt"
    
    func print(i int){
        fmt.Println(i)
    }
    
    func main() {
    
        go print(100)
    
    }
    
    

    猜猜会输出什么?

    image.png

    什么都不会输出,因为 print(100)还没执行完毕,main函数已经结束了,所以它的子协程也不会执行了

    那怎么解决这个问题呢?

    信道可用于在其他协程结束执行之前,阻塞 Go 主协程。请查看信道章节


    go 协程的并发

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func print(num int){
        for  i := 1 ;i < 10 ;i++ {
            fmt.Printf("%d -  第 %d 循环 \n",num, i)
        }
    }
    
    func main() {
    
      go print(1)
      go print(2)
      time.Sleep(1000 * time.Millisecond)
    }
    
    image.png

    由于main 函数 会很快执行完毕 导致子协程 停止工作,所以我们加一个3秒的延时 让main 函数休眠,从而让它的两个个子协程能够执行执行完毕

    相关文章

      网友评论

          本文标题:协程

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