Go基础学习Day02
map
无序的键值对集合,通过key来查询数据,无法决定返回的顺序。
var mapname map[keytype]valueType
make(map[keyType]valueType)
map可以进行动态的增长,len()获取键值对的数目。引用类型,如果有两个赋值的变量,修改其中一个,另一个也会进行改变。不要使用new创建map,会得到一个空引用的指针。
map的遍历
import (
"flag"
"fmt"
)
var mode = flag.String("mode", "", "运行模式,可以设置为fast")
func main() {
scan := make(map[string]int)
scan["cat"] = 6
scan["dog"] = 4
scan["pig"] = 960
for k, v := range scan {
fmt.Println(k, "-----", v)
}
}
删除 delete
delete(mapname,key)
重新定义一个空的map就可以进行所有的清空。map在并发情况下只读是安全的
线程安全的map
sync.map的特性
- 无需初始化,直接声明
- sync.map不能使用map的方式进行取值和设置,store表示存储,load表示获取,delete表示删除
- 使用range配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,range参数中回调函数的返回值在需要继续迭代遍历时,返回true。
var sc sync.Map
sc.Store("cat",6)
sc.Store("dog",960)
fmt.Println(sc.Load("dog"))
sc.Range(func(k,v interface{}) bool{
fmt.Println("iterate:",k,v)
return true
})
//遍历时要提供一个匿名函数,参数为k,v类型为interface,每次range在遍历一个元素的时候,都会调用这个匿名函数将结果返回,
sc.Delete("dog")
nil
指针,切片,映射pointer,通道,函数和接口的零值为nil,nil和其他语言的null不一样:nil标识符不可以进行比较。
new和make
make关键字主要作用是创建slice,map,channel等内置的数据结构,而new的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。
- make分配空间以后会进行初始化,new分配的空间会被清零
- new分配返回的是指针,make返回的是引用
- new可以分配任意类型的数据
函数
结构体
特性:
- 字段必须有自己的类型和值
- 字段名必须唯一
- 字段的类型可以是结构体,或者字段所在结构体的类型 只有当结构体实例化的时候,才会真正的分配内存
创建指针结构体
ins:=new(T)
取结构体地址实例化
ins:=&T{}--->相当于一次new的实例化操作
匿名结构体:
没有类型名称
方法
结构体的方法:接收器类型可以是任何类型,不仅仅是结构体类型,任何类型都可以有方法,甚至可以是函数类型,但是接收器不能是一个接口类型,因为接口是一个抽象定义,而方法确实具体的实现,接收器也不可以是一个指针类型,但他可以是任何其他允许类型的指针。一个类型加上他的方法就是面向对象中的类
import "fmt"
type Bag struct {
items []int
}
func (b *Bag) insert(itemId int) {
b.items = append(b.items, itemId)
}
func main() {
b := new(Bag)
b.insert(100)
fmt.Println(b.items)
}
每个方法只能有一个接收器。
- 指针接收器:this或者self,可以修改变量,复制地址,大对象
- 非指针接收器:获取成员值,但是不能修改,复制值,小对象
给任意类型添加方法
先使用type自定义类型,再在新的type上定义方法。
匿名字段
结构体可以包含一个或多个匿名字段,没有显示的名称,但是有字段的类型,这个时候类型也就是字段的名字。 有继承的功能,可以调用自身内嵌结构体的方法。有重写的感觉。
接口
是一种类型,一种抽象类型。定义了一个规则,只关心行为,不关心数据
- 方法名:当方法名首字母是大写的时候,并且这个接口的首字母也是大写的时候,这个方法可以被接口所在的包之外的代码访问。
接口实现条件
如果一个任意类型T的方法集为一个接口类型的方法集的超集,就说T实现了这个接口类型。
- 接口的方法和实现接口的类型方法格式一致,只实现接口部分方法也不行。
import "fmt"
type DataWriter interface {
WriteData(data interface{}) error
}
type file struct {
}
func (d *file) WriteData(data interface{}) error{
fmt.Println("writedata",data)
return nil
}
func main(){
f:=new(file)//实例化类型
var wr DataWriter//声明接口
wr=f//将接口赋值给实例,也就是*file对象
wr.WriteData("date")
}
类型和接口的关系
一个类型可以实现多个接口,多个类型实现同一个接口
空接口:没有任何方法,所有类型默认实现空接口
空接口类型的变量可以存储所有类型的变量
- 可以作为函数的参数
- 可以作为map的值
类型断言
x.(T)返回bool类型,可以通过switch进行判断。
I/O操作
常用接口Reader,writer,Closer
- io.reader表示一个读取器,
import (
"fmt"
"io"
"os"
)
func main() {
file, err := os.Open("./xxx.txt")
if err != nil {
fmt.Println("open file err:", err)
return
}
defer file.Close()
var buf [128]byte
var content []byte
for {
n, err := file.Read(buf[:])
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read file err", err)
return
}
content = append(content, buf[:n]...)
fmt.Println(string(content))
}
}
bufio
实现带缓冲区的读写,是对文件读写的封装。
go mod
项目中使用:get init ,tidy,install
并发:
一个线程可以跑多个协程。
goroutine
在语言层面中内置了调度和上下文切换的机制。
func main() {
go hello()
fmt.Println("done")
time.Sleep(time.Second * 2)
}
func hello() {
fmt.Println("hello")
}
多个协程:
func main() {
for i := 0; i < 10; i++ {
go hello(i)
}
fmt.Println("done")
time.Sleep(time.Second * 2)
}
func hello(i int) {
fmt.Println("hello", i)
}
主协程退出则子协程也会退出。
GMP模型
调动系统,
p:管理者goroutine的队列,存储当前goroutine的上下文环境,以及会对自己管理的队列及逆行调度管理。
M:go运行时对操作系统内核线程的虚拟
- p与m一一对应,当一个G长久阻塞在M上,runtime就会创建一个新的M,将其他的p挂载在新的M上,回收旧的M。
runtime包
- runtime.Gosched():让出cpu时间,重新等待安排任务。
- runtime.Goexit()结束协程。
channel
Go语言的并发模型CSP,提倡通过通信共享内存,channel可以让一个goroutine发送特定值到另一个goroutine的通信机制,遵循先入先出的规则。
var ch1 chan int
- 创建通道
var ch1 chan int
- 声明:使用make初始化函数之后才可以使用
ch3:=make(chan int)
channel的操作
定义一个通道
ch := make(chan int)
- 发送
ch<-10//将10发送到ch中,
- 接受
x := <-ch //从ch通道中接收
- 关闭
close(ch)
无缓冲通道&同步通道
只有有人接受的时候,才可以发送,会被阻塞直到被接收
func main() {
ch := make(chan int)
ch <- 10
fmt.Println("发送成功")
}
正确:有地方进行接收
func main() {
ch := make(chan int)
go receive(ch)
ch <- 10
fmt.Println("发送成功")
}
func receive(ch chan int) {
s := <-ch
fmt.Println("s=", s)
}
有缓冲通道
指定容量,表示可以存放元素的数量
func main() {
ch := make(chan int, 3)
ch <- 10
fmt.Println("发送成功")
}
如何优雅的使用channel
select
同时响应多个通道的操作
并发安全和锁
waitGroup,mutex,RWMutex,
原子操作:
针对基本类型。