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的应用场景。
所谓的弱,就是一种罪,所谓理想,只是同时拥有实力的人才能说的“现实”