3-并发编程之-runtime包

Go的运行时包runtime中包含了一些列的可以设置go运行时的环境的函数,比如运行最大有多少逻辑处理器(P),最多可以创建多少OS线程(M)等

// 常量
GOOS:目标操作系统

// 方法
// 1 GOROOT():返回本机的GO路径

// 2 Version():返回Go的版本字符串。它要么是递交的hash和创建时的日期;要么是发行标签如"go1.3"

//3 GC():GC执行一次垃圾回收。


//4 Goexit:退出当前goroutine(但是defer语句会照常执行)
Goexit终止调用它的go协程。其它go协程不会受影响。Goexit会在终止该go协程前执行所有defer的函数。
在程序的main go协程调用本函数,会终结该go协程,而不会让main返回。因为main函数没有返回,程序会继续执行其它的go协程。如果所有其它go协程都退出了,程序就会崩溃

//5 Gosched:让当前协程让出cpu以让其他协程运行
Gosched使当前go程放弃处理器,以让其它go程运行。它不会挂起当前go程,因此当前go程未来会恢复执行

//6 GOMAXPROCS:设置最大的可同时使用的CPU核数

GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。本函数在调度程序优化后会去掉

//7 NumCPU:返回当前系统的CPU核数量
返回本地机器的逻辑CPU个数
1.物理CPU数:主板上实际插入的CPU数量,可以数不重复的 physical id 有几个(physical id)
2.CPU核数:单块CPU上面能处理数据的芯片组的数量,如双核、四核等(cpu cores)
3.逻辑CPU数:一般情况下,逻辑CPU=物理CPU数 x 每颗核数,如果不相等的话,则表示服务器的CPU支持超线程技术(简单来说,它可使处理器中的一颗内核如两颗内核那样在操作系统中发挥作用。这样一来,操作系统可使用的执行资源扩大了一倍,大幅度提高了系统的整体性能,此时逻辑CPU=物理CPU个数 x 物理CPU个数 x 2) (processor 0-n)

//8 NumGoroutine:返回真该执行和排队的任务总数
NumGoroutine返回当前存在的Go协程数



一 runtime.Gosched()

让出CPU时间片,重新等待安排任务

package main

import (
	"fmt"
	"runtime"
)

func main() {
	go func(s string) {
		for i := 0; i < 2; i++ {
			fmt.Println(s)
		}
	}("hello world")

	for i := 0; i < 2; i++ {
		// 用于让出CPU时间片,让出当前goroutine的执行权限,调度器安排其它等待的任务运行,
		runtime.Gosched()  // 如果注释掉这行,lqz is nb先打印
		fmt.Println("lqz is nb")
	}
}

二 runtime.Goexit()

退出当前协程

package main

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	go func(s string) {
		fmt.Println("第一次打印:",s)
		runtime.Goexit() // 退出当前协程
		fmt.Println("第二次打印:",s)
	}("hello world")

	fmt.Println("主协程打印")
	time.Sleep(2*time.Second)
}

三 runtime.GOMAXPROCS

Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中的n)。

Go语言中可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。

Go1.5版本之前,默认使用的是单核心执行。Go1.5版本之后,默认使用全部的CPU逻辑核心数。

我们可以通过将任务分配到不同的CPU逻辑核心上实现并行的效果,这里举个例子:

package main

import (
	"fmt"
	"runtime"
	"time"
)

func taskA() {
	for i := 1; i < 10; i++ {
		fmt.Println("taskA:", i)
		time.Sleep(1*time.Millisecond)

	}
}

func taskB() {
	for i := 1; i < 10; i++ {
		fmt.Println("taskB:", i)
		time.Sleep(1*time.Millisecond)
	}
}

func main() {
	runtime.GOMAXPROCS(1)
	go taskA()
	go taskB()
	time.Sleep(2*time.Second)
}

两个任务只有一个逻辑核心,此时是做完一个任务再做另一个任务。 将逻辑核心数设为2,此时两个任务并行执行,代码如下。

package main

import (
	"fmt"
	"runtime"
	"time"
)

func taskA() {
	for i := 1; i < 10; i++ {
		fmt.Println("taskA:", i)
		time.Sleep(1*time.Millisecond)

	}
}

func taskB() {
	for i := 1; i < 10; i++ {
		fmt.Println("taskB:", i)
		time.Sleep(1*time.Millisecond)
	}
}

func main() {
	runtime.GOMAXPROCS(2)
	go taskA()
	go taskB()
	time.Sleep(2*time.Second)
}

Go语言中的操作系统线程和goroutine的关系:

  • 1.一个操作系统线程对应用户态多个goroutine。
  • 2.go程序可以同时使用多个操作系统线程。
  • 3.goroutine和OS线程是多对多的关系,即m:n。

四 其他

package main

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	go func() {
		time.Sleep(1*time.Second)
	}()
	fmt.Println(runtime.NumCPU()) // 返回当前系统的CPU核数量
	fmt.Println(runtime.NumGoroutine()) //返回真该执行和排队的任务总数 -- 2
	fmt.Println(runtime.GOOS) //目标操作系统
	fmt.Println(runtime.GOROOT()) //返回本机的GO路径
	fmt.Println("主协程打印")
	time.Sleep(2*time.Second)
}
posted @ 2022-03-12 15:47  刘清政  阅读(205)  评论(0编辑  收藏  举报