7 文件操作、单元测试、goroutine【Go语言教程】

news2024/11/18 7:37:17

7 文件操作、单元测试、goroutine【Go语言教程】

1 文件操作

1.1 介绍

在这里插入图片描述

  • os.File 封装所有文件相关操作,File 是一个结构体
    常用方法:
  1. 打开文件
    在这里插入图片描述
  2. 关闭文件
    在这里插入图片描述
package main

import (
	"fmt"
	"os"
)

func main(){
	//打开文件
	//file又叫做:file对象、file指针、file文件句柄
	file, err := os.Open("D:/系统默认/桌面/code.txt")
	if err != nil {
		fmt.Println("open file err=", err)
	}
	//输出file,看看file到底是什么,最后结果可以看处file就是一个指针*File
	//file=&{0xc000004a00}
	fmt.Printf("file=%v", file)
	//关闭文件
	err = file.Close()
	if err != nil {
		fmt.Println("close file err=", err)
	}
}

1.2 应用实例

①读文件

常用方法:
①bufio.NewReader(), reader.ReadString【带缓冲】
②io/ioutil【一次性读取,适用于小文件】

  1. 读取文件的内容并显示在终端(带缓冲区的方式),使用 os.Open, file.Close, bufio.NewReader(), reader.ReadString 函数和方法.
package main

import (
	"fmt"
	"os"
	"bufio"
	"io"
)

//定义缓冲区常量
const (
	defaultBufSize = 4096 //默认缓冲区为4096
)


func main(){
	//打开文件
	file, err := os.Open("D:/系统默认/桌面/code.txt")
	if err != nil {
		fmt.Println("open file err=", err)
	}
	//当函数退出时,要及时的关闭file句柄,否则会又内存泄漏问题
	defer file.Close() 
	//创建一个*Reader(带缓冲的)
	reader := bufio.NewReader(file)
	//循环读取文件内容
	for {
		str, err := reader.ReadString('\n') //读取到一个换行就结束
		if err == io.EOF { //io.EOF表示文件的末尾
			break
		}
		//输出读取到的内容
		fmt.Println(str)
	}
	fmt.Println("文件读取结束....")
}

  1. 读取文件的内容并显示在终端(使用 ioutil 一次将整个文件读入到内存中),这种方式适用于文件不大的情况。相关方法和函数(ioutil.ReadFile)
package main

import (
	"fmt"
	"io/ioutil"
)

func main(){
	//使用ioutil.ReadFile一次性将文件读取到位
	file := "D:/系统默认/桌面/code.txt"
	content, err := ioutil.ReadFile(file) // content: []byte
	if err != nil {
		fmt.Printf("read file err=%v", err)
	}
	//把读取到的文件内容显示到终端
	fmt.Printf("%v", string(content))//[]byte
	//我们没有显示的Open文件,因此也不需要显示的Close文件
	//因为,文件的Open何Close都被封装到了ioutils.ReadFile的函数内部
}

效果:
在这里插入图片描述

②写文件

在这里插入图片描述

1. 创建一个新文件,写入内容[os.O_CREATE]
  • file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666)//只写、创作
  • writer:= bufio.NewWriter(file)
package main

import (
	"fmt"
	"os"
	"bufio"
)



func main(){
	//创建一个新文件,写入内容 5句 "hello Go"
	//1. 打开文件 D:/系统默认/桌面/demo1.txt
	filePath := "D:/系统默认/桌面/demo1.txt"
	//os.O_WRONLY | os.O_CREATE, 文件模式 0666 权限操作
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return
	}
	//及时关闭file句柄
	defer file.Close()
	//准备写入5句 "hello, Go"
	str := "hello, Go\n"
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存,因此在调用WriterString方法时,其实内容是先写入到缓存的,
	//所以需要调用Flush方法,将缓冲的数据真正写入到文件中,否则文件中会没有数据
	writer.Flush()
}
2. 覆盖原来的内容写[os.O_TRUNC]

打开一个存在的文件中,将原来的内容覆盖成新的内容 10 句 “你好,Go”

  • file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_TRUNC, 0666)
  • os.O_WRONLY | os.O_TRUNC
package main

import (
	"fmt"
	"os"
	"bufio"
)


func main(){
	//打开一个存在的文件,将原来的内容覆盖为"hello jackson"
	//1. 打开一个已经存在的文件"D:\系统默认\桌面\demo1.txt"
	filePath := "d:/系统默认/桌面/demo1.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_TRUNC, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return
	}
	//及时关闭file句柄
	defer file.Close()
	str := "hello jackson\n"
	//写入时使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString(str)
	}
	//将缓存中的数据flush到硬盘
	writer.Flush()

}
3. 追加写[os.O_APPEND]

打开一个存在的文件,在原来的内容追加内容

  • file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_APPEND, 0666)
package main

import (
	"fmt"
	"os"
	"bufio"
)


func main(){
	//打开一个存在的文件,追加写入"you are good!!!"
	filePath := "d:/系统默认/桌面/demo1.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_APPEND, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return
	}
	defer file.Close()
	str := "you are good!!!\n"
	writer := bufio.NewWriter(file);
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	writer.Flush()

}
4. 读出文件内容并追加[os.O_RDWR | os.O_APPEND]

打开一个存在的文件,将原来的内容读出显示在终端,并且追加 5 句"you are good!!!"

  • file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666)
  • reader := bufio.NewReader(file)
  • str, err := reader.ReadString(‘\n’)
package main

import (
	"fmt"
	"os"
	"bufio"
	"io"
)

func main(){
	//打开一个存在的文件,追加写入"you are good!!!"
	filePath := "d:/系统默认/桌面/demo1.txt"
	//READ、WRITE、APPEND
	file, err := os.OpenFile(filePath, os.O_RDWR | os.O_APPEND, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return
	}
	defer file.Close()
	//先读取原来的内容,并显示在终端
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF { //如果读取到文件的末尾
			break
		}
		//显示到终端
		fmt.Print(str)
	}
	//追加写入数据"you are good!!!"
	str := "you are good!!!"
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	writer.Flush()
}
5. 将A文件内容写入到B文件

编程一个程序,将一个文件的内容,写入到另外一个文件。注:这两个文件已经存在了.

  • 说明:使用 ioutil.ReadFile / ioutil.WriteFile 完成写文件(覆盖写)的任务.
package main

import (
	"fmt"
	_ "os"
	_ "bufio"
	_ "io"
	"io/ioutil"
)



func main(){
	//将demo.txt文件的内容导入到demo1.txt
	//1. 先将demo.txt内容读取到内存
	//2. 将读取到的内容写入到demo1.txt
	filePath1 := "d:/系统默认/桌面/demo.txt"
	filePath2 := "d:/系统默认/桌面/demo1.txt"
	data, err := ioutil.ReadFile(filePath1)
	if err != nil {
		fmt.Printf("read file err=%v\n", err)
		return
	}
	//从demo.txt文件中读取到的data数据写入到demo1.txt
	err = ioutil.WriteFile(filePath2, data, 0666)
	if err != nil {
		fmt.Printf("write file err=%v\n", err)
	}
}
6. 判断文件是否存在

在这里插入图片描述

  • _, err := os.Stat(path)
  • os.IsNotExist(err)
package main

import (
	"fmt"
	"os"
)



func main(){
	//判断文件或目录是否存在
	filePath1 := "d:/系统默认/桌面/demo.txt"
	flag, err := PathExists(filePath1)
	//flag=true, err=<nil>
	fmt.Printf("flag=%v, err=%v\n", flag, err)
}

func PathExists(path string) (bool,  error) {
	_, err := os.Stat(path)
	if err == nil { //文件或目录存在
		return true, nil
	}
	//如果文件的Stat返回的err是os.IsNotExist则表明文件不存在
	if os.IsNotExist(err){
		return false, nil
	}
	return false, err
}

③拷贝文件、统计字符数、命令行参数

1. 拷贝一张图片

说明:将一张图片/电影/mp3 拷贝到另外一个文件 e:/abc.jpg io 包

  • func Copy(dst Writer, src Reader) (written int64, err error)
    注意; Copy 函数是 io 包提供的.
package main

import (
	"fmt"
	"os"
	"io"
	"bufio"
)



func main(){
	//D:\系统默认\桌面
	_, err := CopyFile("D:/系统默认/桌面/banner2.png", "D:/系统默认/桌面/banner.png")
	if err != nil {
		fmt.Printf("CopyFile() fail, err=%v", err)
	} else {
		fmt.Printf("success...")
	}
}

//拷贝文件
func CopyFile(dstFilePath string, srcFilePath string) (written int64, err error){
	srcFile, err := os.Open(srcFilePath)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
	}
	defer srcFile.Close()
	//通过srcFile获取到Reader
	reader := bufio.NewReader(srcFile)
	//打开dstFilePath
	dstFile, err := os.OpenFile(dstFilePath, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open fil err=%v\n", err)
		return
	}
	//通过dstFile获取到Writer
	writer := bufio.NewWriter(dstFile)
	defer dstFile.Close()
	//调用io包下的工具类
	return io.Copy(writer, reader)
}
2. 统计字符数

说明:统计一个文件中含有的英文、数字、空格及其它字符数量

package main

import (
	"fmt"
	"os"
	"io"
	"bufio"
)


//定义一个结构体,用于保存统计结果
type CharCount struct {
	ChCount int //记录英文个数
	NumCount int //数字个数
	SpaceCount int //空格个数
	OtherCount int //其他字符个数
}
func main(){
	//1. 打开一个文件,创建一个Reader
	//2. 每读取一行,就去统计该行有多少个英文、数字、空格和其他字符
	//3. 然后将结果保存到一个结构体
	fileName := "D:/系统默认/桌面/demo.txt"
	file, err := os.Open(fileName)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return
	}
	defer file.Close()
	//定义CharCount实例
	var count CharCount
	//创建一个reader
	reader := bufio.NewReader(file)
	//开始循环读取文件的内容
	for {
		str1, err := reader.ReadString('\n') //一行一行读取
		if err == io.EOF { //读到文件末尾就退出
			break
		}
		//为了兼容中文字符,可以将str转成[]rune
		str := []rune(str1)
		//遍历str,进行统计
		for _, v := range str {
			switch {
				case v >= 'a' && v <= 'z':
					fallthrough //穿透,大小写字母都存在ChCount中
				case v >= 'A' && v <= 'Z':
					count.ChCount++
				case v == ' ' || v == '\t':
					count.SpaceCount++
				case v >= '0' && v <= '9':
					count.NumCount++
				default:
					count.OtherCount++
			}
		}
	}
	//输出统计的结果看看是否正确
	fmt.Printf("字符的个数为=%v, 数字的个数为=%v, 空格的个数为=%v, 其他字符的个数为=%v", 
		count.ChCount, count.NumCount, count.SpaceCount, count.OtherCount)
}
3. 命令行参数解析[flag包]

比如:cmd>main.exe -f c:/aaa.txt -p 200 -u root 这样的形式命令行,go 设计者给我们提供了 flag包,可以方便的解析命令行参数,而且参数顺序可以随意

package main

import (
	"fmt"
	"flag"
)

func main(){
	//定义几个变量,用于接收命令行的参数值
	var user string
	var pwd string
	var host string
	var port int

	//&user 就是接收用户命令行中输入的 -u 后面的参数值
	//"u", 就是 -u 指定参数
	//"", 默认值
	//"用户名,默认为空" 说明
	flag.StringVar(&user, "u", "", "用户名, 默认为空")
	flag.StringVar(&pwd, "pwd", "", "密码, 默认为空")
	flag.StringVar(&host, "h", "localhost", "主机名, 默认为localhost")
	flag.IntVar(&port, "port", 3306, "端口号, 默认为3306")
	//这里有一个非常重要的操作, 转换, 必须调用该方法
	flag.Parse()
	
	//输出结果
	fmt.Printf("user=%v pwd=%v host=%v port=%v", user, pwd, host, port)
}

在这里插入图片描述
mysql的命令行也是类似于这种方式

④json的序列化、反序列化

1. 序列化
  • json 序列化是指,将有 key-value 结构的数据类型(比如结构体、map、切片)序列化成 json 字符串的操作。
  • 这里我们介绍一下结构体、map 和切片的序列化,其它数据类型的序列化类似。
package main

import (
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string `json:"name"`
	Age int		`json:"age"`
	Birthday string
	Sal float64
	Skill string
}

func testStruct(){
	//演示
	monster := Monster{
		Name: "牛魔王",
		Age : 500,
		Birthday : "2011-11-11",
		Sal : 8000.0,
		Skill : "牛魔拳",
	}
	//将monster序列化
	data, err := json.Marshal(&monster)
	if err != nil {
		fmt.Printf("序列号错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("结构体:monster序列化后=%v\n", string(data))
}

//将map进行序列化
func testMap(){
	var a map[string]interface{}
	//使用map之前需要make
	a = make(map[string]interface{})
	a["name"] = "红孩儿"
	a["age"] = 30
	a["address"] = "洪崖洞"
	//将a这个map进行序列化
	data, err := json.Marshal(a)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	fmt.Printf("map序列化=%v\n", string(data))
}

//对切片序列化 []map[string]interface{}
func testSlice(){
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	//使用map前make
	m1 = make(map[string]interface{})
	m1["name"] = "jack"
	m1["age"] = "7"
	m1["address"] = "北京"
	slice = append(slice, m1)

	var m2 map[string]interface{}
	//使用map前,需要先make
	m2 = make(map[string]interface{})
	m2["name"] = "tom"
	m2["age"] = "20"
	m2["address"] = [2]string{"墨西哥", "夏威夷"}
	slice = append(slice, m2)

	data, err := json.Marshal(slice)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	//输出序列化后的结果
	fmt.Printf("切片:slice序列化后=%v\n", string(data))
}

//对基本数据类型序列化【意义不大】
func testFloat64(){
	var num1 float64 = 2345.67
	data, err := json.Marshal(num1)
	if err != nil {
		fmt.Printf("序列化错误 err=%v\n", err)
	}
	fmt.Printf("基本数据类型:num1 序列化后=%v\n", string(data))
}

func main(){
	testStruct()
	testMap()
	testSlice()
	testFloat64()
}

在这里插入图片描述

2. 反序列化

json 反序列化是指,将 json 字符串反序列化成对应的数据类型(比如结构体、map、切片)的操作

package main

import (
	"fmt"
	"encoding/json"
)

//定义一个结构体
type Monster struct {
	Name string `json:"name"`
	Age int		`json:"age"`
	Birthday string
	Sal float64
	Skill string
}

//反序列化结构体
func UnmarshalStruct(){
	//说明 str 在项目开发中,是通过网络传输获取到..  或者是读取文件获取到
	str := "{\"Name\":\"牛魔王\",\"Age\":500,\"Birthday\":\"2011-11-11\",\"Sal\":8000,\"Skill\":\"牛魔拳\"}"
	var monster Monster
	err := json.Unmarshal([]byte(str), &monster)
	if err != nil {
		fmt.Printf("unmarshal err=%v", err)
	}
	fmt.Printf("结构体反序列化后 monster=%v monster.Name=%v\n", monster, monster.Name)
}

//反序列化map
func UnmarshalMap(){
	str := "{\"address\":\"洪崖洞\",\"age\":30,\"name\":\"红孩儿\"}"
	var a map[string]interface{}
	//注意:反序列化map,不需要make,因为make操作被封装到Unmarshal函数
	err := json.Unmarshal([]byte(str), &a)
	if err != nil {
		fmt.Printf("unmarshal err=%v", err)
	}
	fmt.Printf("map反序列化后 a=%v\n", a)
}

//反序列化切片
func UnmarshalSlice(){
	str := "[{\"address\":\"北京\",\"age\":\"7\",\"name\":\"jack\"}," +
		"{\"address\":[\"墨西哥\",\"夏威夷\"],\"age\":\"20\",\"name\":\"tom\"}]"
	var slice []map[string]interface{}
	//反序列化不需要make,因为make操作被封装在了Unmarshal函数
	err := json.Unmarshal([]byte(str), &slice)
	if err != nil {
		fmt.Printf("unmarshal err=%v\n", err)
	}
	fmt.Printf("切片反序列化后 slice=%v\n", slice)
}

func main(){
	UnmarshalStruct()
	UnmarshalMap()
	UnmarshalSlice()
}

在这里插入图片描述
说明:
在这里插入图片描述

2 单元测试

2.1 概念

Go 语言中自带有一个轻量级的测试框架 testing 和自带的 go test 命令来实现单元测试和性能测试,testing 框架和其他语言中的测试框架类似,可以基于这个框架写针对相应函数的测试用例,也可以基于该框架写相应的压力测试用例。

  • go test
  • testing 测试框架
    go test [运行错误时才有日志]
    go test -v [不论运行正确还是错误,都输出日志]
  1. 测试用例文件名必须以 _test.go 结尾。 比如 cal_test.go , cal 不是固定的。
  2. 测试用例函数必须以 Test 开头,一般来说就是 Test+被测试的函数名,比如 TestAddUpper
  3. TestAddUpper(t *tesing.T) 的形参类型必须是 *testing.T 【看一下手册】
  4. 一个测试用例文件中,可以有多个测试用例函数,比如 TestAddUpper、TestSub
  5. 运行测试用例指令
    (1) cmd>go test [如果运行正确,无日志,错误时,会输出日志]
    (2) cmd>go test -v [运行正确或是错误,都输出日志]
  6. 当出现错误时,可以使用 t.Fatalf 来格式化输出错误信息,并退出程序
  7. t.Logf 方法可以输出相应的日志
  8. 测试用例函数,并没有放在 main 函数中,也执行了,这就是测试用例的方便之处[原理图].
    在这里插入图片描述
  9. PASS 表示测试用例运行成功,FAIL 表示测试用例运行失败
  10. 测试单个文件,一定要带上被测试的原文件
    go test -v cal_test.go cal.go
  11. 测试单个方法
    go test -v -test.run TestAddUpper

快速入门:

xxx_test.go文件应该与被测试的函数在同一个包下
比如:都在monster包下,在进行单元测试的时候如果用到了其他go文件中的函数、结构体、变量等,需要在go test命令后面再加上这个函数。
执行类似下面命令便可以了
go test -v .\monster_test.go .\monster.go
在这里插入图片描述

package test

import (
	"testing"
)

func TestSub(t *testing.T){
	res := Sub(20, 10)
	if res != 10 {
		t.Fatalf("sub(20, 10) error, 期望值为%v, 实际值为%v", 10, res)
	}
	t.Logf("test sub success...")
}

//定义一个Sub函数
func Sub(a int, b int) int {
	return a - b
}

在这里插入图片描述

2. 2 实战

在这里插入图片描述

  1. monster/monster.go:
package monster
import (
	"fmt"
	"encoding/json"
	"io/ioutil"
)


type Monster struct {
	Name string
	Age int
	Skill string
}

//序列化保存到文件中
func (this *Monster) Store() bool {
	//1. 序列化
	data, err := json.Marshal(this)
	if err != nil {
		fmt.Println("marshal err=", err)
		return false
	}
	//2. 保存到文件
	filePath := "d:/系统默认/桌面/demo.ser"
	err = ioutil.WriteFile(filePath, data, 0666)
	if err != nil {
		fmt.Println("write file err=", err)
		return false
	}
	return true
}

//反序列化
func (this *Monster) ReStore() bool {
	//1. 从序列化文件中读取字符串
	filePath := "d:/系统默认/桌面/demo.ser"
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Println("ReadFile err=", err)
		return false
	}
	//2. 使用读取到的data: byte[]进行反序列化
	err = json.Unmarshal(data, this)
	if err != nil {
		fmt.Println("Unmarshal err=", err)
		return false
	}
	return true
}
  1. monster/monster_test.go
package monster
import (
	"testing"
)

//测试Store方法
func TestStore(t *testing.T){
	monster := &Monster{
		Name : "红孩儿",
		Age: 10,
		Skill: "吐火",
	}
	flag := monster.Store()
	if !flag {
		t.Fatalf("monster.Store()错误,希望为=%v 实际为%v", true, flag)
	}
	t.Logf("monster.Store() 测试成功!!!")
}

func TestReStore(t *testing.T){
	var monster = &Monster{}
	flag := monster.ReStore()
	if !flag {
		t.Fatalf("monster.ReStore()错误, 希望为=%v 实际为=%v", true, flag)
	}
	//进一步判断
	if monster.Name != "红孩儿" {
		t.Fatalf("monster.ReStore()错误, 希望为=%v 实际为=%v", "红孩儿", monster.Name)
	}
	t.Logf("monster.ReStore() 测试成功!!!")
}

测结果:
在这里插入图片描述

3 goroutine

3.1 goroutine(协程)

①概念及快速入门

1. 概念
  • Go 主线程(有程序员直接称为线程/也可以理解成进程): 一个 Go 线程上,可以起多个协程,你可以这样理解,协程是轻量级的线程[编译器做优化]。
  • Go 协程的特点
  1. 有独立的栈空间
  2. 共享程序堆空间
  3. 调度由用户控制
  4. 协程是轻量级的线程
    在这里插入图片描述
  1. 进程、线程关系:
    在这里插入图片描述
    在这里插入图片描述
  2. 并发与并行
  • 多线程程序在单核上运行,就是并发
  • 多线程程序在多核上运行,就是并行
2. 快速入门

请编写一个程序,完成如下功能:

  1. 在主线程(可以理解成进程)中,开启一个 goroutine, 该协程每隔 1 秒输出 “hello,world”
  2. 在主线程中也每隔一秒输出"hello,golang", 输出 10 次后,退出程序
  3. 要求主线程和 goroutine 同时执行.
package main
import(
	"fmt"
	"time"
	"strconv"
)

//编写一个函数,每隔1s输出 "hello world"
func test(){
	for i := 1; i <= 10; i++ {
		fmt.Println("test() hello world "+ strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

func main(){
	go test() //开启了一个goroutine 协程
	for i := 1; i <= 10; i++ {
		fmt.Println("main() hello, golang " + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

在这里插入图片描述
流程图:
在这里插入图片描述

小结:

  1. 主线程是一个物理线程,直接作用在 cpu 上的。是重量级的,非常耗费 cpu 资源。
  2. 协程从主线程开启的,是轻量级的线程,是逻辑态。对资源消耗相对小。
  3. Golang 的协程机制是重要的特点,可以轻松的开启上万个协程。其它编程语言的并发机制是一般基于线程的,开启过多的线程,资源耗费大,这里就突显Golang 在并发上的优势了

②gorountine的MPG调度模型

  1. 概念:

M:操作系统主线程(main)
P:协程执行所需上下文(Process context)
G:协程(goroutine)
在这里插入图片描述

  1. 运行状态
  • 状态1:
    在这里插入图片描述
  • 状态2:
    在这里插入图片描述

③设置cpu个数

介绍:为了充分了利用多 cpu 的优势,在 Golang 程序中,设置运行的 cpu 数目
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/517500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VPGTrans: 10%的成本定制你自己的类GPT-4多模态大模型

作者 | 张傲 最近的多模态&#xff08;对话&#xff09;大模型将基于文本的ChatGPT的强大能力扩展到了多模态输入&#xff0c;实现强大的多模态语义理解&#xff0c;比如GPT-4、BLIP-2、Flamingo等。但咱们普通玩家训练一个多模态GPT代价非常昂贵。来自于新加坡国立大学和清华大…

基于SSM框架的核酸检测管理系统

基于SSM框架的核酸检测管理系统 快速查看 基于SSM框架的核酸检测管理系统功能需求开发工具模块相关技术系统相关图片 功能需求 用户模块&#xff1a; 注册功能&#xff1a;普通用户可以访问本系统进行账户注册&#xff0c;个人资料&#xff1a;登录系统对自己的个人资料&…

Parker派克伺服电机有哪些优势特点?如何选型?

一、什么是伺服电机&#xff1f; 伺服电机是一种可以通过控制器精确地控制位置、速度和加速度的电机&#xff0c;主要由电机、编码器和控制器三部分组成&#xff0c;具有高转矩、高精度、快速响应和低转速稳定特性&#xff0c;能够在负载扰动、电压变化及机械特性变化下保持较…

springboot整合邮箱功能二(普通邮件, html邮件, thymleaf邮件)

【SpringBoot整合Email发送邮件】_ζั͡ ั͡空 ั͡ ั͡白&#xfffd;的博客-CSDN博客 https://www.cnblogs.com/erlou96/p/16878192.html#_label1_5 1. 准备工作 1.1 qq邮箱设置 本文默认使用qq邮箱来发送邮件,然后使用一个在线临时邮箱来接收邮件。为了让程序能够通过…

srs one2one,one2many通话环境搭建

一、简介 二、go环境配置 三、srs编译配置 四、信令服务器编译 4.1 signaling8 4.2 web服务器 五、测试 六、附录 官⽅⽂档参考地址&#xff1a;https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#sfu-one-to-one 一、简介 srs的webrtc能力和两个信令服务器不管是逻辑上还是代码…

Linux 进程基础

目录 1、进程的概念 2、进程与线程的区别、进程与程序的区别 2.1 进程与线程的区别 2.2 进程与程序的区别 3、进程相关 shell 命令 3.1 ps 3.3.1 参数说明 3.3.2 结果说明 3.2 pidof 3.3 pstree 3.4 top 3.5 kill 4、进程相关函数 4.1 fork 4.1.1 fork的函数原型…

医院内导航及智能导医,医院导诊图怎么制作?

在大型综合性医院&#xff0c;由于专业分工精细&#xff0c;一个诊疗过程涉及的功能单元往往分布在不同的楼宇、不同楼层的不同位置&#xff0c;再加上多数患者对医院环境不熟悉&#xff0c;导致滞院的时间长、诊疗效率低、患者对服务的满意度下降。为解决这一问题&#xff0c;…

VMware Aria Operations for Logs 8.12 - 集中式日志管理

VMware Aria Operations for Logs 8.12 - 集中式日志管理 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-aria-operations-for-logs/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 集中式日志管理 VMware Aria …

蓝牙mesh数据包格式解析

蓝牙mesh数据包的结构如下图&#xff1a; 总长31个字节。 Length (1Byte)&#xff1a;数据长度 Type (1Byte)&#xff1a;广播类型 IVI (1bit)&#xff1a;用来认证加密Network PDU的IV index的最低位 NID (7bits)&#xff1a;网络ID&#xff0c;network ID。从网络密钥(Ne…

学习了两个多月就进了我们公司,顺利过了试用期,我心塞了...

转行起因 公司前段时间来了个大专机械专业毕业的&#xff0c;挺好奇他在如今这个环境下怎么进来的而且非本科非科班&#xff0c;后面我请他喝了一次酒&#xff0c;我才了解到他的故事&#xff0c;写出来与大家分享&#xff0c;希望对各位有点启迪。 他以前在一个大厂做售后工…

新来的00后真卷,我想离职了···

都说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。 这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作没两年&#xff0c;跳槽到我们公司起薪20K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。…

sentinel基本原理以及核心类介绍

Sentinel 核心类解析 架构图 ProcessorSlotChain Sentinel 的核心骨架&#xff0c;如上图结构&#xff0c;将不同的 Slot 按照顺序串在一起&#xff08;责任链模式&#xff09;&#xff0c;从而将不同的功能&#xff08;限流、降级、系统保护&#xff09;组合在一起。slot ch…

2023.05.11-利用GPT4free免费使用ChatGPT4

1. 简介 现在OpenAI&#xff0c;虽然出了ChatGPT4&#xff0c;但是只给plus会员用&#xff0c;对于国内的用户来说&#xff0c;不仅需要魔法&#xff0c;还需要有一张外网的信用卡来开通会员&#xff0c;使用起来重重不便&#xff0c;有一种想要花钱买服务&#xff0c;都找不到…

tf卡文件隐藏怎样恢复,原来有这三种方法,你了解多少呢?

TF卡是一种便携式存储设备&#xff0c;非常方便用于存储数据。但是&#xff0c;有时TF卡中的数据会被不小心隐藏了&#xff0c;也许是误操作&#xff0c;也许是病毒攻击等原因。所以&#xff0c;下面将讲述如何找回TF卡中被隐藏的数据。 【一】关于TF卡概述 TF卡&#xff08;…

uniapp打包ios保姆式教程【最新】

uniapp打包 打包方式ios打包一、前往官网登录二、添加证书 三、添加标识符(Identifiers)四、添加安装ios测试机(Devices)五、获取证书profile文件六、生成并下载p12文件七、开始打包 打包方式 安卓打包直接使用公共测试证书即可打包成功&#xff0c;简单方便&#xff0c;这里我…

【数据库数据恢复】sql server数据库无法附加查询的数据恢复案例

数据库数据恢复环境&#xff1a; 一台Dell PowerEdge某型号存储&#xff0c;数块SAS硬盘分别组建raid1和raid5两组磁盘阵列。其中2块磁盘组建的RAID1&#xff0c;用于安装操作系统&#xff1b;其余几块磁盘组建raid5&#xff0c;用于存放数据。 上层安装的windows服务器&#x…

vue中动态使用vh calc设置高度

动态设置高度 <div class"user_menu_box" :style"menuHeight"><!-- 用户信息 --><div class"user_info" :class"{ menu-collase: isCollapse }"><imgsrc"/assets/login_images/bg.jpg"alt"暂无图…

【python数据分析】对python开发岗位需求进行分析可视化

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 什么是数据分析 明确目的–获得数据(爬虫&#xff0c;现有&#xff0c;公开的数据)–数据预处理——数据可视化——结论 准备 环境使用&#xff1a; 在开始写我们的代码之前&#xff0c;我们要准备好运行代码的程序 Anacon…

空中下载技术(OTA)电控信息安全

随着汽车电子控制系统功能复杂度和数据颗粒度呈阶梯式增加&#xff0c;其发展速度逐渐超越网络安全防护方法、技术和标准的发展&#xff0c;现阶段汽车电子正面临巨大的网络信息安全风险&#xff0c;对功能安全的潜在影响也仍在探索和解决中&#xff0c;信息安全问题已经成为影…

maven从入门到精通 第五章 在IDEA2023中使用Maven

这里写自定义目录标题 一 Maven基础回顾1 archetype2 指定自己的maven工程所在的位置3 接下来是用文本编辑器打开自己下载的maven文件下的 conf >settings 二 创建maven子工程1 配置环境&#xff0c;测试运行2 打包maven的三种方式2.1 点击maven左侧的lifecycle2.2 点击m标签…