Go基础学习Day02

164 阅读6分钟

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的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。

  1. make分配空间以后会进行初始化,new分配的空间会被清零
  2. new分配返回的是指针,make返回的是引用
  3. 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)
}

每个方法只能有一个接收器。

  1. 指针接收器:this或者self,可以修改变量,复制地址,大对象
  2. 非指针接收器:获取成员值,但是不能修改,复制值,小对象

给任意类型添加方法

先使用type自定义类型,再在新的type上定义方法。

匿名字段

结构体可以包含一个或多个匿名字段,没有显示的名称,但是有字段的类型,这个时候类型也就是字段的名字。 有继承的功能,可以调用自身内嵌结构体的方法。有重写的感觉。

接口

是一种类型,一种抽象类型。定义了一个规则,只关心行为,不关心数据

  • 方法名:当方法名首字母是大写的时候,并且这个接口的首字母也是大写的时候,这个方法可以被接口所在的包之外的代码访问。

接口实现条件

如果一个任意类型T的方法集为一个接口类型的方法集的超集,就说T实现了这个接口类型。

  1. 接口的方法和实现接口的类型方法格式一致,只实现接口部分方法也不行。
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")
}

类型和接口的关系

一个类型可以实现多个接口,多个类型实现同一个接口

空接口:没有任何方法,所有类型默认实现空接口

空接口类型的变量可以存储所有类型的变量

  1. 可以作为函数的参数
  2. 可以作为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运行时对操作系统内核线程的虚拟

  1. 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
  1. 创建通道
var ch1 chan int
  1. 声明:使用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,

原子操作:

针对基本类型。