7

Golang 协程(goroutine) 运行过程 与 并发

 3 years ago
source link: https://surest.cn/archives/167/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

首先我们需要理解进程和线程的关系

当运行一个应用程序的时候,操作系统会为这个应用程序启动一个进程,该个进程包含了应用程序在运行中所用需要用到和维护的各种资源的容器

  • 每个进程至少包含一个线程
  • 每个进程的初始线程被称为主线程
  • 执行线程的空间是应用程序本身的空间
  • 主线程终止,应用程序也将终止

goroutine执行过程

前提,goroutine的执行主要依靠调度处理器来完成,如

// 创建了两个调度处理器
runtime.GOMAXPROCS(2)

1、创建一个goroutine
2、goroutine进入调度处理器全局运行队列(调度器)中
3、调度器分配一个调度处理器供goroutine使用
4、goroutine 执行

在其上述执行过程中,我们很容易会思考到一个问题,例如现在有 3 个goroutine等待执行,那么,goroutine是如何运行的呢

我们来尝试执行一段代码

import (
"fmt"
"runtime"
"sync"
)

func main()  {
    // 创建一个调度处理器
    runtime.GOMAXPROCS(1)

    var wg sync.WaitGroup
    wg.Add(2)

    fmt.Println("协程开始 ...\n")

    go func() {
        defer wg.Done()

        for count :=0; count < 3; count++ {
            for char := 'a'; char < 'a' + 26; char ++ {
                fmt.Printf("%c", char)
            }
        }
    }()

    go func() {
        defer wg.Done()

        for count :=0; count < 3; count++ {
            for char := 'A'; char < 'A' + 26; char ++ {
                fmt.Printf("%c", char)
            }
        }
    }()

    fmt.Println("wait ...\n")
    wg.Wait()

    fmt.Println("\n结束...")
}
协程开始 ...
wait ...
ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
结束...

我们会发现,是不是第二goroutine先执行完毕?是的,不管是尝试多少次,都会是这个结果

其实,导致这个问题的原因来自于其管理goroutine寿命的行为

goroutine中,为了防止某个goroutine运行时间过长,调度器会停止当前正在运行的goroutine,给其他goroutine运行的机会

继续查看代码

package main

import (
"fmt"
"runtime"
"sync"
)

func main()  {
    runtime.GOMAXPROCS(1)

    var wg sync.WaitGroup
    wg.Add(3)

    fmt.Println("协程开始 ...\n")

    go func() {
        defer wg.Done()

        fmt.Println(1)
    }()

    go func() {
        defer wg.Done()

        fmt.Println(2)
    }()
    go func() {
        defer wg.Done()

        fmt.Println(3)
    }()

    fmt.Println("wait ...\n")
    wg.Wait()

    fmt.Println("\n结束...")
}

结果则为 3 1 2

并发与并行

不用的程序在不同的物理处理器上执行,关键的在于同时做很多事情

使用较少的资源做更多的事情,即在go中为,用聪明的算法根据单个物理机器,调度一个个执行

go中,如何实现并行


// 双核
runtime.GOMAXPROCS(2)

即可

以上,会触发2两调度处理器,并发运行,但实际上这种并发也其实就是 go 通过单个物理机器创建多个线程实现的伪并行

IO操作下的goroutine

IO下的goroutine都为阻塞性的goroutine

  • 资源读写IO

调用时,线程将从逻辑处理器上分离,线程继续堵塞,处理器将绑定一个新的线程,并执行其他 goroutineIO goroutine 执行完毕后,占用线程进行回收,下次使用

  • 网络IO

网络IO,将从逻辑处理器上分离,且将其放入到网络轮询器的运行中,当检测到改资源IO操作就绪,将取出并分配到逻辑处理器上重新运行

调度器配置

Go 默认支持最多创建10000个线程,如果使用的再多,可能会崩溃,可以通过runtime或者debug的包来完成这些配置

本文由 邓尘锋 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Mar 26, 2021 at 06:13 pm


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK