go强大的多线程能力很强大,能并发处理大量的任务。
主要介绍go中的进程、线程、协程这三个东西。它们的关系按照内存大小的关系依次是进程 > 线程 > 协程(一般一个协程2K)。
进程基本上是一个正在执行的程序,它是操作系统中最小的资源分配单位。比如电脑上运行的一个软件就是一个进程。
go开启进程的方式有三种,本质上都是通过命令去启动电脑上的软件比如nodejs。
- package utils
-
- import (
- "fmt"
- "io"
- "os"
- "os/exec"
- "syscall"
- )
-
- func Process() {
- // 创建进程方式一
- grepCmd := exec.Command("node", "-v") // cmd命令
- grepIn, _ := grepCmd.StdinPipe() // 写入管道
- grepOut, _ := grepCmd.StdoutPipe() // 输出管道
- grepCmd.Start() // 开始执行命令
- grepIn.Write([]byte("写入的数据")) // 写入的数据
- grepIn.Close() // 关闭写入通道
- grepBytes, _ := io.ReadAll(grepOut) // 读取输出的数据
- grepCmd.Wait() // 等待进程进程
- fmt.Println("> grep hello, pid: ", grepCmd.Process.Pid)
- fmt.Println(string(grepBytes))
-
- // 创建进程方式二
- nodeCmd := exec.Command("node", "-v")
- cmdOutput, err := nodeCmd.Output()
- if err != nil {
- fmt.Println(err)
- } else {
- fmt.Println(cmdOutput, string(cmdOutput)) // buffer转utf8
- }
-
- binary, lookErr := exec.LookPath("node")
- if lookErr != nil {
- panic(lookErr)
- }
- args := []string{"node", "-v"}
- env := os.Environ()
- execErr := syscall.Exec(binary, args, env)
- if execErr != nil {
- panic(execErr)
- }
-
- println("process进程")
- }
线程可以看成是轻量级的进程,线程内存大小会根据处理器进行自动分配。
其中根据go的两种gc方式cgo和go线程数量是不同的,其中cgo会释放掉空闲的线程提高性能,比如有用到的net包中的LookupHost方法。
go:GODEBUG=netdns=go go run main.go
cgo:GODEBUG=netdns=cgo go run main.go
- package utils
-
- import (
- "fmt"
- "runtime"
- "runtime/pprof"
- "sync"
- )
-
- var threadProfile = pprof.Lookup("threadcreate")
-
- func ThreadTest() {
- fmt.Printf(("线程数%d个"), threadProfile.Count())
-
- var wg sync.WaitGroup
- wg.Add(10) // 计时器如果为0则所有Wait上阻塞的协程都会释放,为负数则会panic
- for i := 0; i < 10; i++ {
- go func() {
- defer wg.Done()
- println("执行任务")
- runtime.LockOSThread() // 杀掉线程(如果协程在退出的时候没有unlock这个线程,那么这个线程会被终止)
- }()
- }
- wg.Wait() // 等待线程执行结束
- fmt.Printf(("线程数%d个"), threadProfile.Count())
- println("thread线程")
- }
这里就和channel(https://blog.csdn.net/qq_40816649/article/details/132898741?spm=1001.2014.3001.5501)一起说吧,协程可以看成是轻量级线程,go多线程能力强处理并发靠的就是协程。
在go中启动一个协程很简单在方法前面加上go关键词,就会启动一个runtime运行函数和当前的go线程不在用一个线程,所以这里为了数据安全go提倡通过通信(channel)共享内存而不是通过共享内存而实现通信。
- package utils
-
- import (
- "fmt"
- "time"
- )
-
- func worker(id int, jobs <-chan int, results chan<- int) {
- for j := range jobs {
- fmt.Println("worker", id, "processing job", j)
- time.Sleep(time.Second)
- results <- j * 2
- }
- }
- func Goroutine() {
- jobs := make(chan int, 100)
- results := make(chan int, 100)
- for w := 1; w <= 3; w++ {
- // go关键词启动一个新的runtime运行函数worker(和当前的go线程不在同一个栈)(协程)
- go worker(w, jobs, results)
- }
- for j := 1; j <= 9; j++ {
- jobs <- j
- }
- for a := 1; a <= 9; a++ {
- <-results
- }
- println("goroutine轻量级线程也叫协程")
- }
go的学习之路还很长期望后续工作中能碰到go的应用场景。
所谓的弱,就是一种罪,所谓理想,只是同时拥有实力的人才能说的“现实”