Go语法和特性

news/2024/4/26 4:23:22/文章来源:https://blog.csdn.net/penngo/article/details/130354500

文章目录

    • 1、语法基础
      • 1.1 环境安装
        • 1.1.1 安装
        • 1.1.2 编译工具
      • 1.2 基本语法
        • 1.2.1 变量的声明与初始化
        • 1.2.1 原生数据类型
        • 1.2.2 指针
        • 1.2.3 常量与类型别名
        • 1.2.4 分支与循环
      • 1.3 容器
        • 1.3.1 数组
        • 1.3.2 列表
        • 1.3.2 字典
        • 1.3.3 容器遍历
      • 1.4 函数与接口
        • 1.4.1 函数声明和参数传递
        • 1.4.2 匿名函数和闭包
          • 1.4.2.1 匿名函数
          • 1.4.2.2 闭包
        • 1.4.3 接口声明与嵌套
        • 1.4.4 函数体实现接口
      • 1.5 结构体和方法
        • 1.5.1 结构体的定义
        • 1.5.2 结构体的实例化和初始化
        • 1.5.3 方法与接收器
        • 1.5.4 结构体实现接口
        • 1.5.5 内嵌和组合
    • 2、特性
      • 2.1 依赖管理
        • 2.1.1 包管理
        • 2.1.2 GOPATH
        • 2.1.3 Go Modules
      • 2.2 反射基础
        • 2.2.1 reflect.Type
        • 2.2.2 reflect.StruceField反射字段和reflect.Method反射方法
        • 2.2.3 reflect.Value反射值
      • 2.3 并发
        • 2.3.1 协程goroutine
        • 2.3.2 通道channel
        • 2.3.3 sync同步锁
          • 2.3.3.1 Mutex(互斥锁)
          • 2.3.3.2 RWMutex(读写锁)
          • 2.3.3.3 WaitGroup(读写锁)
          • 2.3.3.4 Mapp(并发安全字典)

1、语法基础

1.1 环境安装

1.1.1 安装

Go语言中文网:https://studygolang.com/dl
Go语言英文网:https://go.dev/
标准库文档:https://pkg.go.dev/std
Linux安装
下载:https://go.dev/dl/go1.20.2.linux-amd64.tar.gz

# 解压
tar -zxvf go1.20.2.linux-amd64.tar.gz -C /usr/local# 设置环境变量
export PATH=$PATH:/usr/local/go/bin# 设置依赖包下载源国内源,国内可用的镜像源,可选设置其中一个
export GOPROXY=https://goproxy.cn      # 七牛
export GOPROXY=https://goproxy.io
export GOPROXY=https://mirrors.aliyun.com/goproxy/  # 阿里云

Windows安装
下载:https://go.dev/dl/go1.20.2.windows-amd64.zip
同样解压和设置类似环境变量

1.1.2 编译工具

(1)go run命令

go run HelloGo.go

(1)go build命令

# -o指定生成的可执行文件名
go build -o HelloGo HelloGo.go
# 或者
go build HelloGo.go

1.2 基本语法

1.2.1 变量的声明与初始化

每一个声明的变量必须被使用,否则会编译不通过,变量声明样式:

# 格式
var 名称 类型
# 例如
var a int         // 声明一个int类型的变量
var b string      // 声明一个string类型的变量
var c []float     // 声明一个float类型的切片
var d struct{     // 声明一个匿名结构体,结构体内有一个int字段x int
}
var e func() bool  // 声明一个函数变量
# 推导语法糖特性,精简的样式
var a = 100
# :=左值必须是未定义的变量,同时不能出现在全局变量的声明和初始化中
b := "Hello"

1.2.1 原生数据类型

1、整数

  • 有符号:int8、int16、int32、int64
  • 无符号:uint8、uint16、uint32、uint64
  • 自匹配长度:int、uint
    2、浮点数
  • float32、float64
    3、布尔型
    true、false
    字符串
    字符串以原生数据类型出现,类同整型、布尔型,基于UTF-8编码实现,需要区分byte和rune。
	f := "Go编程"fmt.Printf("byte len is %v\n", len(f))fmt.Printf("rune len is %v\n", utf8.RuneCountInString(f))for _, g := range []byte(f) {fmt.Printf("byte for:%c\n", g)}for _, h := range f {fmt.Printf("rune for:%c\n", h)}

1.2.2 指针

包含三个概念:指针类型、指针地址、指针取值

str := "Hello World"
strPrt := &str
fmt.Printf("类型: %T, 地址值: %v, 取值: %v", strPrt, strPrt, *strPrt)
#输出结果: 类型: *string, 地址值: 0xc000054250, 变量值: Hello World

1.2.3 常量与类型别名

常量定义

const str string = "Hello"
const str2 = "Hello"

类型别名

type myname1 = int  // 定义一个类型别名
type mynate2 int    // 定义一个新类型
func main(){var a myname1 = 1vra b mynate2 = 2
}

1.2.4 分支与循环

if 条件两边的“()”可以省略

if 条件 {//执行的代码
} else if 条件 {//执行的代码
}

switch 不需要break跳出,如果需要继续执行后边的case判断,需要加fallthrough,case支持数值常量、字符串、表达式的处理
如果在case中使用判断表达式,switch后边不需要指定判断变量

score := 90
switch {
case score < 100 && score >= 90:fmt.Println("优秀")
default:fmt.Println("分数错误")}

for,go没有提供while、do while,可以使用break跳出循环,使用continus继续下一个循环,使用样式

for 初始值;条件;结束{循环代码
}

1.3 容器

1.3.1 数组

语法格式:

# var 变量名 [数量]类型
var classMates1 [3]string
classMates1[0] = "Hello"
classMates1[1] = "World"classMates2  := [...]string{"Hello", "World"}

切片

classMates3 := classMates2[0,1] // "Hello"

动态切片

// 格式 make([]类型, 长度, 容量)
a = make([]int, 2, 4)    // 动态

1.3.2 列表

定义样式

import ("container/list"
)
// 格式
//var name list.List // 或
//name := list.New()func main()  {tmpList := list.New()// 插入for i:= 1 ; i <= 10 ; i++ {tmpList.PushBack(i)}first := tmpList.PushFront(0)// 删除tmpList.Remove(first)// 遍历for l := tmpList.Front(); l != nil; l = l.Next(){fmt.Print(l.Value, " ")}
}

1.3.2 字典

定义格式

//name = make(map[类型]类型)
classMates1 := make(map[string]string)
classMates1["a"] = "Hello"  // 添加映射关系
classMates2 := map[string]string{"a": "Hello",}
mate,ok := classMates2[1] // 如果存在,ok为true

1.3.3 容器遍历

package main
import ("fmt"
)func main()  {// 数组的遍历nums := [...]int{1,2,3,4,5,6,7,8}for k, v:= range nums{// k为下标,v为对应的值fmt.Println(k, v, " ")}// 切片的遍历slis := []int{1,2,3,4,5,6,7,8}for k, v:= range slis{// k为下标,v为对应的值fmt.Println(k, v, " ")}// 字典的遍历tmpMap := map[int]string{0 : "小明",1 : "小红",2 : "小张",}for k, v:= range tmpMap{// k为键值,v为对应值fmt.Println(k, v, " ")}}

1.4 函数与接口

1.4.1 函数声明和参数传递

函数格式

// 格式
func 函数名(参数名 类型,...)(返回参数 类型,...){//函数体
}// 例子
func cal(a, b int) int{ // 如果类型相比,可省略a类型return a + b;
}
func div(a, b int)(returna, returnb int){returna = a/b;returnb = a%b;return;
}

1.4.2 匿名函数和闭包

1.4.2.1 匿名函数
# 样式
funce(params)(return params){// 函数体
}# 例子
func (name string){fmt.println("My name is ", name)
}# 匿名函数赋值给变量
myFunc := func(){fmt.Println(time.Now())
}
currentTime()

回调函数使用

func proc(input string, processor func(str string)){processor(input)
}
func main(){proc("hello", func(str string){for _, v := range str{fmt.Printf("%c\n",v)}})
}
1.4.2.2 闭包
// 闭包内封装了myvar内部变量,外部代码无法直接访问
func createCounter(myvar int) func() int {if myvar < 0{myvar = 0}// 引用myvar,创建一个闭包return func() int{myvar++// 返回当前计数return myvar;}
}func main()  {// 计数器1c1 := createCounter(1)fmt.Println(c1()) // 2fmt.Println(c1()) // 3// 计数器2c2 := createCounter(10)fmt.Println(c2()) // 11fmt.Println(c1()) // 4
}

1.4.3 接口声明与嵌套

定义样式,定义需要使用type和interface关键词

type 接口名称 interface{方法名(变量)(return 变量)
}// 例子
type Tank interface{Walk()Fire()
}

1.4.4 函数体实现接口

// 接口定义
type Printer interface{Print(interface}{)
}// 函数定义为类型
type FuncCaller func(p interface{})// 实现Printer的Print方法
func (funcCaller FuncCaller) Print(p interface{}) {// 调用funcCaller函数本体funcCaller(p)
}func main()  {var printer Printer// 将匿名函数强转为FuncCaller赋值给printerprinter = FuncCaller(func(p interface{}) {fmt.Println(p)})printer.Print("Golang is Good!")
}

1.5 结构体和方法

1.5.1 结构体的定义

// 定义,
type 结构名 struct{变量 类型变量 类型
}// 例子
Type Person struct{ // 结构体在包外被访问,结构名首字母大写Name string    //字段是公开的,字段名首字母必须大写Birth stringID int64
}

1.5.2 结构体的实例化和初始化

  • 像声明基本类型一样实例化结构体
var p1 Person
p1.Name = "Hello"
p1.Birth = "2023-04-06"
  • 使用new方式为结构体分配内存,这时返回的为结构体的指针,同样用.访问字段
p2 := new(Person)
p2.Name = "Hello"
p2.Birth = "2023-04-06"
  • 实例化时也可以对字段进行初始化
p4 := Person{Name = "Hello"Birth = "2023-04-06"
} 
  • 全部字段初始化时,可以按字段顺序初始化值,并省略键值
p4 := Person{"Hello""2023-04-06"5
} 

1.5.3 方法与接收器

方法定义样式

func (recipient recipientType) methodName(params)(return params){// function body
}

例子

type Person struct {Name  string Birth string ID    int64  
}
func (person *Person) changeName(name string) {person.Name = name
}func (person Person) changeId(id int64) {person.ID = id
}func (person Person) printMess() {fmt.Printf("My name is %v, and my birthday is %v, and my id is %v\n",person.Name, person.Birth, person.ID)
}
func main() {p1 := Person{Name:  "Hello",Birth: "1991-10-23",ID:    1,}p1.printMess()p1.changeName("World")p1.changeId(2)p1.printMess()}

1.5.4 结构体实现接口

用结构体实现接口

// Cat接口
type Cat interface {// 抓老鼠CatchMouse()
}// Dog接口
type Dog interface {// 吠叫Bark()
}type CatDog struct {Name string
}//  实现Cat接口
func (catDog *CatDog) CatchMouse()  {fmt.Printf("%v caught the mouse and ate it!\n", catDog.Name)
}// Dog接口
func (catDog *CatDog) Bark()  {fmt.Printf("%v barked loudly!\n", catDog.Name)
}func main()  {catDog := &CatDog{"Lucy",}// 声明一个Cat接口,并将catDog指针类型赋值给catvar cat Catcat = catDogcat.CatchMouse()// 声明一个Dog接口,并将catDog指针类型赋值给dogvar dog Dogdog = catDogdog.Bark()
}

1.5.5 内嵌和组合

定义

type 名称 struct{string // 类型int
}

结构体内嵌,可以直接访问内嵌结构体成员

type Wheel struct {shape string
}
type Car struct {WheelName string
}
func main()  {car := &Car{Wheel{"圆形的",},"福特",}fmt.Println(car.Name, car.shape, " ")
}

可以通过结构体内嵌模拟面向对象的继承特性。

type Swimming struct {
}func (swim *Swimming) swim()  {fmt.Println("swimming is my ability")
}type Flying struct {
}func (fly *Flying) fly()  {fmt.Println("flying is my ability")
}type WildDuck struct {SwimmingFlying
}type DomesticDuck struct {Swimming
}func main()  {wild := WildDuck{}wild.fly()wild.swim()domestic := DomesticDuck{}domestic.swim()
}

2、特性

2.1 依赖管理

2.1.1 包管理

同一个包内定义的函数、类型、结构、变量、常量,都能被包内的其它代码直接访问,非同包内需要import导入。
如果函数、类型、结构、变量、常量位于不同包下,需要将它们的名称首字母大写,才能被其它包访问。
package定义包

package main

import导入包

import ("fmt""os"
)

2.1.2 GOPATH

Go工作目录结构:

  • src:源码,一个目录就是一个包;
  • pkg:编译后的类库;
  • bin:编译后的可执行程序;
    GOPATH是Go语言的一个环境变量。
  • go install:编译Go项目代码,并安装到GOPATH目录。
    如果是包含main包和main函数,生成可执行文件到GOPATH/bin,可以直接运行;
    如果没有,则生成.a应用包到GOPATH/pkg目录,可以被依赖调用;
  • go get:拉取依赖包。
    go get通过git或svn下载远程代码到GOPATH/src目录,然后自动执行go install完成依赖编译和安装。

2.1.3 Go Modules

1.12版本中正式支持Go Modules,包管理解决方案,只要包含go.mod文件即可。
创建新的Module:

go mod init 模块名

当前目录将会生成go.mod内容:

module test1go 1.18

如果需要引入新的依赖test2,版本1.0.0,在go.mod中加入代码:

module test1go 1.18require github.com/test2 v1.0.0

下载依赖命令

# 手动下载依赖
go mod download# 更新依赖关系,只下载缺少的模块,并移除不用的模块
go mod tidy

2.2 反射基础

通过反射,可以获取变量的名称、类型信息、结构信息。例子

import ("fmt""reflect"
)// 定义一个人的接口
type Person interface {SayHello(name string)Run() string
}type Hero struct {Name stringAge intSpeed int
}
func (hero *Hero) SayHello(name string)  {fmt.Println("Hello " + name, ", I am " + hero.Name)
}
func (hero *Hero) Run() string {fmt.Println("I am running at speed ", hero.Speed)return "Running"
}

2.2.1 reflect.Type

通过reflect.TypeOf()获取类型对象reflect.Type

typeOfHero := reflect.TypeOf(Hero{})
fmt.Printf("Hero's type is %s, kind is %s", typeOfHero, typeOfHero.Kind())// 结构体类型为Struct;指针类型为Ptr,可以使用Type.Elem获取真实类型对象
fmt.Printf("*Hero's type is %s, kind is %s",reflect.TypeOf(&Hero{}), reflect.TypeOf(&Hero{}).Kind())
typeOfPtrHero := reflect.TypeOf(&Hero{})
fmt.Printf("*Hero's type is %s, kind is %s\n",typeOfPtrHero, typeOfPtrHero.Kind())
typeOfHero := typeOfPtrHero.Elem()
fmt.Printf(" typeOfPtrHero elem to typeOfHero, Hero's type is %s, kind is %s", typeOfHero, typeOfHero.Kind())

2.2.2 reflect.StruceField反射字段和reflect.Method反射方法

  • Type接口提供的反射字段的方法
// 获取结构体内的字段数量
NumField() int
// 获取字段类型对象
Field(i int) StructField
// 根据字段名称获取字段类型对象
FieldByName(name string) (StructField, bool)
  • 字段reflect.StructField的属性
type StructField struct{
// 字段的名称
Name string
// 字段的类型
Type Type
// Tag, 如果ID string    'json:"id"'   
Tag StructTag
// 字节偏移
Offset uintptr
// 字段的index
Index []int
// 字段是否公开
Anonymous bool
}

使用例子

typeOfHero := reflect.TypeOf(Hero{})
// 通过 #NumField 获取结构体字段的数量
for i := 0 ; i < typeOfHero.NumField(); i++{fmt.Printf("field' name is %s, type is %s, kind is %s\n",typeOfHero.Field(i).Name,typeOfHero.Field(i).Type,typeOfHero.Field(i).Type.Kind())
}
// 获取名称为 Name 的成员字段类型对象
nameField, _ := typeOfHero.FieldByName("Name")
fmt.Printf("field' name is %s, type is %s, kind is %s\n", nameField.Name, nameField.Type, nameField.Type.Kind())
  • Type接口提供的反射方法的方法
// 方法的数量
NumMethod() int
// 按索引号查找方法
Method(int) Method
// 按方法名查找方法
MethodByName(string) (Method, bool)
  • 方法reflect.Method的属性
type Method struct{
// 方法名 
Name string
// 类型
Type Tyoe
// 反射对象,可用于调用方法
Func Value
// 方法的索引号
Index int
}

使用例子

// 声明一个 Person 接口,并用 Hero 作为接收器
var person Person = &Hero{}
// 获取接口Person的类型对象
typeOfPerson := reflect.TypeOf(person)
// 打印Person的方法类型和名称
for i := 0 ; i < typeOfPerson.NumMethod(); i++{fmt.Printf("method is %s, type is %s, kind is %s.\n",typeOfPerson.Method(i).Name,typeOfPerson.Method(i).Type,typeOfPerson.Method(i).Type.Kind())
}
method, _ := typeOfPerson.MethodByName("Run")
fmt.Printf("method is %s, type is %s, kind is %s.\n", method.Name, method.Type, method.Type.Kind())

2.2.3 reflect.Value反射值

  • 获取Value反射值
// 使用reflect.ValueOf获取反射值
reflect.ValueOf()
  • 类型Value的方法
func (v Value) Interface() (i interface{})
// 返回int值
func (V Value) Int() int64
// 返回float值
func (v Value) Float() float64
// 返回[]byte值
func (v Value) Bytes() []byte
// 返回string值
func (v Value) String() string
// 返回bool值
func (v Value) Bool bool

使用例子

name := "小明"
valueOfName := reflect.ValueOf(name)
fmt.Println(valueOfName.Interface())// 取值类型不匹配,提示panic错误
name := "小明"
valueOfName := reflect.ValueOf(name)
fmt.Println(valueOfName.Bytes())typeOfHero := reflect.TypeOf(Hero{})
heroValue := reflect.New(typeOfHero)
fmt.Printf("Hero's type is %s, kind is %s\n", heroValue.Type(), heroValue.Kind())// 修改Value值
name := "小明"
valueOfName := reflect.ValueOf(&name)
valueOfName.Elem().Set(reflect.ValueOf("小红"))
fmt.Println(name)// 通过CanAddr判断是否可修改
name := "小明"
valueOfName := reflect.ValueOf(name)
fmt.Printf( "name can be address: %t\n", valueOfName.CanAddr())
valueOfName = reflect.ValueOf(&name)
fmt.Printf( "&name can be address: %t\n", valueOfName.CanAddr())
valueOfName = valueOfName.Elem()
fmt.Printf( "&name's Elem can be address: %t", valueOfName.CanAddr())// 通过CanSet判断是否为公开变量,必须同步满足CanAddr和CanSet才能修改值
valueOfHero := reflect.ValueOf(hero).Elem()
valueOfName := valueOfHero.FieldByName("Name")
// 判断字段的 Value 是否可以设定变量值
if valueOfName.CanSet() {valueOfName.Set(reflect.ValueOf("小张"))
}
fmt.Printf("hero name is %s", hero.Name)

调用接口方法例子

var person Person = &Hero{Name: "小红",Speed: 100,
}
valueOfPerson := reflect.ValueOf(person)
// 获取SayHello 方法
sayHelloMethod := valueOfPerson.MethodByName("SayHello")
// 构建调用参数并通过 #Call 调用方法
sayHelloMethod.Call([]reflect.Value{reflect.ValueOf("小张")})
// 获取Run 方法
runMethod := valueOfPerson.MethodByName("Run")
// 通过 #Call 调用方法并获取结果
result := runMethod.Call([]reflect.Value{})
fmt.Printf("result of run method is %s.", result[0])var person Person = &Hero{Name: "小红",
}
// 获取接口Person的类型对象
typeOfPerson := reflect.TypeOf(person)
// 打印Person的方法类型和名称
sayHelloMethod, _ := typeOfPerson.MethodByName("Run")
// 将 person 接收器放在参数的第一位
result:=sayHelloMethod.Func.Call([]reflect.Value{reflect.ValueOf(person)})
fmt.Printf("result of run method is %s.", result[0])// 一般方法的反射调用
methodOfHello := reflect.ValueOf(hello)
methodOfHello.Call([]reflect.Value{})
func hello() {fmt.Print("Hello World!")
}

2.3 并发

2.3.1 协程goroutine

使用goroutine语法格式

go 表达式

使用例子

import ("fmt""time"
)
func test() {fmt.Println("I am work in a single goroutine")
}func main() {go test()time.Sleep(time.Second)
}

匿名函数

func main() {go func(name string) {fmt.Println("Hello " + name)}("world")time.Sleep(time.Second)
}

2.3.2 通道channel

通道作为goroutine之间同步和通信的工具。

# 发送数据到channel使用"<-"符号
chanel <- val
# 从channel读取数据
val, ok:= <- channel# 创建channel
ch := make(chan T, size)

无缓存例子

func printInput(ch chan string)  {for val := range ch{if val == "EOF"{break}fmt.Printf("Input is %s\n", val)}
}
func main()  {// 创建一个无缓冲的 channelch := make(chan string)go printInput(ch)scanner := bufio.NewScanner(os.Stdin)for scanner.Scan() {val := scanner.Text()ch <- valif val == "EOF"{fmt.Println("End the game!")break}}defer close(ch)
}

带缓冲例子

func consume(ch chan int)  {// 线程休息 100s 再从 channel 读取数据time.Sleep(time.Second * 100)<- ch
}
func main()  {// 创建一个长度为 2 的 channelch := make(chan int, 2)go consume(ch)ch <- 0ch <- 1// 发送数据不被阻塞fmt.Println("I am free!")ch <- 2fmt.Println("I can not go there within 100s!")time.Sleep(time.Second)
}

使用select读取

func send(ch chan int, begin int )  {for i :=begin ; i< begin + 10 ;i++{ch <- i}
}
func main()  {ch1 := make(chan int)ch2 := make(chan int)go send(ch1, 0)go send(ch2, 10)// 主 goroutine 休眠 1s,保证调度成功time.Sleep(time.Second)for {select {case val := <- ch1: // 从 ch1 读取数据fmt.Printf("get value %d from ch1\n", val)case val := <- ch2 : // 从 ch2 读取数据fmt.Printf("get value %d from ch2\n", val)case <-time.After(2 * time.Second): // 超时设置fmt.Println("Time out")return}}
}

2.3.3 sync同步锁

2.3.3.1 Mutex(互斥锁)

sync.Mutex方法

Lock()
Unlock()

例子

import ("fmt""sync""time"
)
func main()  {var lock sync.Mutexgo func() {// 加锁lock.Lock()defer lock.Unlock()fmt.Println("func1 get lock at " + time.Now().String())time.Sleep(time.Second)fmt.Println("func1 release lock " + time.Now().String())}()time.Sleep(time.Second / 10)go func() {lock.Lock()defer lock.Unlock()fmt.Println("func2 get lock " + time.Now().String())time.Sleep(time.Second)fmt.Println("func2 release lock " + time.Now().String())}()// 等待 所有 goroutine 执行完毕time.Sleep(time.Second * 4)
}
2.3.3.2 RWMutex(读写锁)

sync.RWMutex提供的方法

// 写加锁
func (rw *RWMutex) Lock()
// 写解锁
func (rw *RWMutex) Unlock()
// 读加锁
func (rw *RWMutex) RLock()
// 读解锁
func (rw *RWMutex) RUnlock()

例子

import ("fmt""strconv""sync""time"
)
func main()  {var rwLock sync.RWMutex// 获取读锁for i := 0 ; i < 5 ;i ++{go func(i int) {rwLock.RLock()defer rwLock.RUnlock()fmt.Println("read func " + strconv.Itoa(i) +" get rlock at " +  time.Now().String())time.Sleep(time.Second)}(i)}time.Sleep(time.Second / 10)// 获取写锁for i := 0 ; i < 5; i++{go func(i int) {rwLock.Lock()defer rwLock.Unlock()fmt.Println("write func " + strconv.Itoa(i) +" get wlock at " +  time.Now().String())time.Sleep(time.Second)}(i)}time.Sleep(time.Second * 10)
}
2.3.3.3 WaitGroup(读写锁)

sync.WaitGroup接口方法

// 添加等待数量,
func(wg *WaitGroup) Add(delta int)
// 等待数量减1
func(wg *WaitGroup) Done()
// 等待
func(wg *WaitGroup) Wait()

例子

import ("fmt""strconv""sync""time"
)
func main()  {var waitGroup sync.WaitGroupwaitGroup.Add(5)for i := 0 ; i < 5 ; i++{go func(i int) {fmt.Println("work " + strconv.Itoa(i) + " is done at " + time.Now().String())// 等待 1 s 后减少等待数time.Sleep(time.Second)waitGroup.Done()}(i)}waitGroup.Wait()fmt.Println("all works are done at " + time.Now().String())
}
2.3.3.4 Mapp(并发安全字典)

sync.Map接口方法

// 删除操作
func (m *Map) Delete(key interface{})
// 读操作
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
// 读取或写入。存在指定的 key 则读取,否则写入
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
// 写操作
func (m *Map) Store(key, value interface{})
// 遍历
func (m *Map) Range(f func(key, value interface{}) bool)

使用例子

import ("fmt""strconv""sync"
)func addNumber(begin int)  {for i := begin ; i < begin + 3 ; i++{syncMap.Store(i, i)}waitGroup.Done()
}
var syncMap sync.Map
var waitGroup sync.WaitGroupfunc main() {routineSize := 5waitGroup.Add(routineSize)for i := 0; i < routineSize; i++ {go addNumber(i * 10)}waitGroup.Wait()var size intsyncMap.Range(func(key, value interface{}) bool {size++//fmt.Println("key-value pair is", key, value, " ")return true})fmt.Println("syncMap current size is " + strconv.Itoa(size))value, ok := syncMap.Load(0)if ok {fmt.Println("key 0 has value", value, " ")}
}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上海车展:预售价109.8万元,仰望U8见证国产品牌崛起

如果要评选2023上海车展上比亚迪展台“最亮的星”&#xff0c;估计很多媒体和观众都会毫不迟疑地把票投给仰望U8。 没办法&#xff0c;因为在本届车展上&#xff0c;仰望U8的表现实在是太吸睛了。 作为比亚迪旗下的高端新能源品牌&#xff0c;仰望汽车在上海车展上携两款车型—…

【Leetcode -141.环形链表 -2.两数相加】

Leetcode Leetcode -141.环形链表Leetcode -2.两数相加 Leetcode -141.环形链表 题目&#xff1a;给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给…

测试Ocr工具IronOCR(续2:编写圈选图片识别文本的程序)

上篇文章介绍了加载图片并圈选图片中文字区域的程序实现方式&#xff0c;本文基于此实现识别圈选区域文字内容的程序。主要识别语言包括英文和中文。IronOCR包中自带英文语言包&#xff0c;项目还需安装中文语言包&#xff0c;建议直接安装IronOcr.Languages.Chinese语言包&…

什么样的测试才是优秀的测试

什么样的测试才是优秀的测试 优秀的测试应该包括以下要素&#xff1a; 测试代码的可读性和可维护性 代码在项目中及特定源代码中的组织方式 测试所检查的内容 测试的可靠性及可重复性 测试对测试替身的使用 可读的代码才是可维护的代码 代码较差的可读性与缺陷密度密切相…

软件测试技术那么多,我们该如何分辨?

经典软件测试技术分类&#xff1a; 测试技术是指顺利完成测试的一系列相关过程&#xff0c;有很多可能的分类方式&#xff0c;表2-1就是其中的一种。表中列出了流行的测试技术&#xff0c;也按照上面的讨论对其进行分类&#xff1a;手工测试、自动测试、静态测试、动态测试、功…

今年SMETA审核费用即将涨价

【今年SMETA审核费用即将涨价】 SMETA全称&#xff08; Sedex Members Ethical Trade Audit &#xff09;&#xff0c;即Sedex会员社会道德贸易审核&#xff0c;它是Sedex发起的一种负责任的供应链审计方法/项目。 Sedex是一个全球性的责任商业平台&#xff0c;SMETA是审核方法…

手推FlinkML2.2(三)

SQLTransformer&#xff08;SQL转换器&#xff09;是一种数据预处理方法&#xff0c;允许您使用SQL语句对数据进行转换和操作。SQL转换器通常用于数据清洗、特征工程和数据聚合等任务&#xff0c;以提高数据分析和机器学习模型的性能。它可以与各种数据处理和存储系统&#xff…

本地搭建属于自己的ChatGPT:基于PyTorch+ChatGLM-6b+Streamlit+QDrant+DuckDuckGo

本地部署chatglm及缓解时效性问题的思路&#xff1a; 模型使用chatglm-6b 4bit&#xff0c;推理使用hugging face&#xff0c;前端应用使用streamlit或者gradio。 微调对显存要求较高&#xff0c;还没试验。可以结合LoRA进行微调。 缓解时效性问题&#xff1a;通过本地数据库…

你的车有通风座椅吗?新款奔驰S400升级原厂主副驾座椅通风

大家好&#xff0c;我是奔之升小志&#xff08;bzs878&#xff09;&#xff0c;专注名车原厂升级&#xff0c;欢迎戳戳右上角“”号关注一下&#xff0c;持续为您带来精彩改装案例。 座椅通风有什么用&#xff1f;能改善身体与座椅接触面空气流通&#xff0c;达到不出汗的效果…

选择美国虚拟主机需注意的安全问题

在选择美国虚拟主机时&#xff0c;安全性应该是您首要关注的问题。虚拟主机通常是网站托管的最便宜和最方便的方式之一&#xff0c;但也存在安全问题。在本文中&#xff0c;我们将讨论一些您应该注意的安全问题&#xff0c;并提供一些解决方案来保护您的网站。 一、了解虚拟主机…

C++(继承(上))

目录 &#xff1a; 1.引出继承的概念 2.继承的关系和方式 3.继承中的作用域 ------------------------------------------------------------------------------------------------------------------------------ 1.引出继承的概念 这些学生、老师、后勤都具有相同的特征&…

elementUI-el-table组件使用总结

一、背景 vue2项目中用到el-table这个组件&#xff0c;但基础的功能不够用&#xff0c;所以需要自定义 二、表头自定义 比如要让表头展现出下面的形式&#xff1a; 只需使用 slot"header" slot-scope"scope" 对插槽进行定义&#xff0c;并绑定变量 <…

CPU Cache:访问存储速度是如何大幅提升的?

我们了解到不同的物理器件&#xff0c;它们的访问速度是不一样的&#xff1a;速度快的往往代价高、容量小&#xff1b;代价低且容量大的&#xff0c;速度通常比较慢。为了充分发挥各种器件的优点&#xff0c;计算机存储数据的物理器件不会只选择一种&#xff0c;而是以 CPU 为核…

java的validation框架(参数校验)

一.bean validation和hibernate validator参数校验常用约束注解&#xff1a; 空值校验类&#xff1a;Null&#xff0c;NotNull&#xff0c;NotEmpty&#xff0c;NotBlank等 范围校验类&#xff1a;Min&#xff0c;Size&#xff0c;Digits&#xff0c;Future&#xff0c;Negati…

微信小程序自定义搜索标题栏

一&#xff1a;需求 把微信小程序标题栏处变成搜索栏。自定义返回上级页面。 二&#xff1a;需求分析 首先要把小程序标题栏设置为可自定义。然后计算原标题栏的高度组成结构。根据计算高度设置搜索框和返回按钮的布局。最后进行代码功能实现。 三&#xff1a;功能实现 1&…

4月19号软件更新资讯合集....

JavaWeb 微服务前后端分离 EurekaEleVue 版 v1.5.0 发布 v1.5.0 更新如下&#xff1a; 1、解决 token 过期无法跳转至登录页的问题&#xff1b; 2、授权服务进行重构与优化&#xff1b; 一款 Java 语言基于 SpringCloud、SpringSecurity、OAuth2、Eureka、Vue、ElementUI、…

Go Fuzzing:发现你未曾发现的漏洞

文章目录 Fuzzing(模糊测试)要求示例模拟crash 总结参考资料 Fuzzing(模糊测试) go fuzz文档 对于软件开发者而言&#xff0c;一项重要的任务就是确保程序的安全性。而其中一种风险就是软件中可能存在的漏洞。传统的测试方法往往需要耗费大量的时间和人力&#xff0c;而使用F…

4月21号软件更新资讯合集.....

PlayEdu v1.0-beta.3 发布&#xff0c;视频培训解决方案 PlayEdu 是基于 SpringBoot3 Java17 React18 开发的企业内部培训系统。它专注于提供私有化部署方案&#xff0c;包括视频&#xff0c;图片等资源的内网部署。目前主要支持有本地视频上传播放、学员邮箱登录、无限级部门…

多数据源 使用 mybatis-plus-generator 3.5.1版本进行代码生成

文章目录 前言多数据源 使用 mybatis-plus-generator 3.5.1版本进行代码生成1. 说明2. 添加依赖2.1. mybatis-plus-generator 自动生成依赖2.2. 多数据源依赖2.3. 建立新项目的完全pom.xml 3. application.yml 多数据源配置 mybatis-plus-generator配置4. 创建一个MybatisPlus…

多通道振弦传感器无线采集仪 数字传感器起始通道分配

多通道振弦传感器无线采集仪 数字传感器起始通道分配 寄存器 DS_CHNUM(299)用于设置读取到的数字传感器数据从哪个通道开始占用&#xff0c;默认为 1。 单个数字传感器占用的通道数量与具体的传感器类型有关&#xff0c;例如&#xff1a;每个激光测距仪会占用 1 个通道&#xf…