• go并发(进程、线程、协程)


    背景

            go强大的多线程能力很强大,能并发处理大量的任务。

    详细案例分类

            主要介绍go中的进程、线程、协程这三个东西。它们的关系按照内存大小的关系依次是进程 > 线程 > 协程(一般一个协程2K)。

    进程

            进程基本上是一个正在执行的程序,它是操作系统中最小的资源分配单位。比如电脑上运行的一个软件就是一个进程。
            go开启进程的方式有三种,本质上都是通过命令去启动电脑上的软件比如nodejs。

    1. package utils
    2. import (
    3. "fmt"
    4. "io"
    5. "os"
    6. "os/exec"
    7. "syscall"
    8. )
    9. func Process() {
    10. // 创建进程方式一
    11. grepCmd := exec.Command("node", "-v") // cmd命令
    12. grepIn, _ := grepCmd.StdinPipe() // 写入管道
    13. grepOut, _ := grepCmd.StdoutPipe() // 输出管道
    14. grepCmd.Start() // 开始执行命令
    15. grepIn.Write([]byte("写入的数据")) // 写入的数据
    16. grepIn.Close() // 关闭写入通道
    17. grepBytes, _ := io.ReadAll(grepOut) // 读取输出的数据
    18. grepCmd.Wait() // 等待进程进程
    19. fmt.Println("> grep hello, pid: ", grepCmd.Process.Pid)
    20. fmt.Println(string(grepBytes))
    21. // 创建进程方式二
    22. nodeCmd := exec.Command("node", "-v")
    23. cmdOutput, err := nodeCmd.Output()
    24. if err != nil {
    25. fmt.Println(err)
    26. } else {
    27. fmt.Println(cmdOutput, string(cmdOutput)) // buffer转utf8
    28. }
    29. binary, lookErr := exec.LookPath("node")
    30. if lookErr != nil {
    31. panic(lookErr)
    32. }
    33. args := []string{"node", "-v"}
    34. env := os.Environ()
    35. execErr := syscall.Exec(binary, args, env)
    36. if execErr != nil {
    37. panic(execErr)
    38. }
    39. println("process进程")
    40. }

    线程(轻量级进程)

            线程可以看成是轻量级的进程,线程内存大小会根据处理器进行自动分配。
            其中根据go的两种gc方式cgo和go线程数量是不同的,其中cgo会释放掉空闲的线程提高性能,比如有用到的net包中的LookupHost方法。
            go:GODEBUG=netdns=go go run main.go
            cgo:GODEBUG=netdns=cgo go run main.go

    1. package utils
    2. import (
    3. "fmt"
    4. "runtime"
    5. "runtime/pprof"
    6. "sync"
    7. )
    8. var threadProfile = pprof.Lookup("threadcreate")
    9. func ThreadTest() {
    10. fmt.Printf(("线程数%d个"), threadProfile.Count())
    11. var wg sync.WaitGroup
    12. wg.Add(10) // 计时器如果为0则所有Wait上阻塞的协程都会释放,为负数则会panic
    13. for i := 0; i < 10; i++ {
    14. go func() {
    15. defer wg.Done()
    16. println("执行任务")
    17. runtime.LockOSThread() // 杀掉线程(如果协程在退出的时候没有unlock这个线程,那么这个线程会被终止)
    18. }()
    19. }
    20. wg.Wait() // 等待线程执行结束
    21. fmt.Printf(("线程数%d个"), threadProfile.Count())
    22. println("thread线程")
    23. }

    协程(轻量级线程)

            这里就和channel(https://blog.csdn.net/qq_40816649/article/details/132898741?spm=1001.2014.3001.5501)一起说吧,协程可以看成是轻量级线程,go多线程能力强处理并发靠的就是协程。
            在go中启动一个协程很简单在方法前面加上go关键词,就会启动一个runtime运行函数和当前的go线程不在用一个线程,所以这里为了数据安全go提倡通过通信(channel)共享内存而不是通过共享内存而实现通信。

    1. package utils
    2. import (
    3. "fmt"
    4. "time"
    5. )
    6. func worker(id int, jobs <-chan int, results chan<- int) {
    7. for j := range jobs {
    8. fmt.Println("worker", id, "processing job", j)
    9. time.Sleep(time.Second)
    10. results <- j * 2
    11. }
    12. }
    13. func Goroutine() {
    14. jobs := make(chan int, 100)
    15. results := make(chan int, 100)
    16. for w := 1; w <= 3; w++ {
    17. // go关键词启动一个新的runtime运行函数worker(和当前的go线程不在同一个栈)(协程)
    18. go worker(w, jobs, results)
    19. }
    20. for j := 1; j <= 9; j++ {
    21. jobs <- j
    22. }
    23. for a := 1; a <= 9; a++ {
    24. <-results
    25. }
    26. println("goroutine轻量级线程也叫协程")
    27. }

    结论

            go的学习之路还很长期望后续工作中能碰到go的应用场景。

    所谓的弱,就是一种罪,所谓理想,只是同时拥有实力的人才能说的“现实”

  • 相关阅读:
    数据治理-数据安全-基本概念
    【电源专题】开关电源的同步与非同步
    大数据之Spark案例实操完整使用(第六章)
    pgz-sbv-gf极狐低代码平台发布
    arx 读入块表
    图的邻接矩阵与搜索
    AlexNet、VGG、GoogLeNet、ReaNet、MobileNet、ShuffleNet和EfficientNet网络的亮点和创新之处
    【LeetCode】删除无效的括号 [H](递归)
    积分商城该如何帮助商家盈利
    统计学习方法 支持向量机(下)
  • 原文地址:https://blog.csdn.net/qq_40816649/article/details/132901240