Golang教程第8篇-map、锁机制

1.map

map是一种key:value数据结构,又叫字典或关联数组

1.1.map声明

声明一个map类型是不分配内存的,必须初始化后才能使用,初始化可以使用make,声明格式有以下几种
var a map[string]string
var a map[string]int
var a map[int]string
var a map[int]int
var a map[string]map[string]string
特别注意最后一种,值也可以是map

1.2.map初始化

make初始化

package main

import (
    "fmt"
)

func Test1() {
    var a map[string]string
    a = make(map[string]string,100)
    a["abc"] = "efg"
    a["abc1"] = "efg"
    a["abc"] = "hhh"
    fmt.Println(a)
}

func main() {
    Test1()
}

编译执行
06go93
注意如果是同一个key赋值,后面的会覆盖前面的

声明时初始化

package main

import (
    "fmt"
)

func Test() {
    var a map[string]string{
        "key":"value"
    }
    a["abc"] = "efg"
    a["abc1"] = "efg"
    a["abc"] = "hhh"
    fmt.Println(a)
}

func main() {
    Test()
}

编译执行
06go94

1.3.map嵌套map

map的value可以是map

package main

import (
    "fmt"
)

func Test() {
    a := make(map[string]map[string]string,100)
    a["key1"] = make(map[string]string)
    a["key1"]["key2"] = "abc"
    a["key1"]["key3"] = "def"
    a["key1"]["key4"] = "ghi"
    fmt.Println(a)
}

func main() {
    Test()
}

06go112

1.4.函数传递map

package main

import (
    "fmt"
)

func Modify(a map[string]map[string]string) {
    _,ok := a["zhanshan"]
    if !ok {
        a["zhangshan"] = make(map[string]string)
    }
    a["zhangshan"]["username"] = "wangteng"
    a["zhangshan"]["password"] = "wangteng"
    return
}

func Test() {
    a := make(map[string]map[string]string,100)
    Modify(a)
    fmt.Println(a)
}

func main() {
    Test()
} 

06go113

1.5.遍历map

package main

import (
    "fmt"
)

func Test() {
    a := make(map[string]map[string]string,100)
    a["key1"] = make(map[string]string)
    a["key1"]["key2"] = "abc"
    a["key1"]["key3"] = "abc"
    a["key1"]["key4"] = "abc"
    a["key1"]["key5"] = "abc"
    
    a["key2"] = make(map[string]string)
    a["key2"]["key3"] = "abc"
    a["key2"]["key4"] = "abc"
    
    Trans(a)
    delete(a,"key1")
    fmt.Println()
    Trans(a)
    
    fmt.Println()
    for j := range a {
        delete(a,j)
    }
    fmt.Println(a)
    fmt.Println(len(a))
}

func Trans(a map[string]map[string]string) {
    for k,v := range a {
        fmt.Println(k)
        for k1,v1 := range v {
            fmt.Println(k1,v1)
        ]
    }
}

func main() {
    Test()
}

编译执行
06go114

1.6.map切片

package main 

import (
    "fmt"
)

func Test() {
    a := make([]map[int]int,5)
    if a[0] == nil {
        a[0] = make(map[int]int)
    }
    a[0][10] = 10
    fmt.Println(a)
}

func main() {
    Test()
}

编译执行
06go115

1.7.map排序

map的key是无序的,这就导致遍历map时也是无序的,如果要按照特定的需求进行排序,可以通过以下方式实现

  • 先获取key
  • 对key排序
  • 按照排好序的key来遍历

排序前

package main

import (
    "fmt"
    "sort"
)

func TestMapSort() {
    var a map[int]int
    a = make(map[int]int,5)
    
    a[1] = 1
    a[2] = 2
    a[3] = 3
    a[4] = 4
    
    for k,v := range a {
        fmt.Println(k,v)
    }
}

func main() {
    TestMapSort()
}

编译执行
06go116

排序后

package main

import (
    "fmt"
    "sort"
)

func TestMapSort() {
    var a map[int]int
    a = make(map[int]int,5)
    
    a[1] = 1
    a[2] = 2
    a[3] = 3
    a[4] = 4
    
    var keys []int
    for k,_ := range a {
        keys = append(keys,k)
    }
    
    sort.Ints(keys)
    
    for _,v := range keys {
        fmt.Println(v,a[v])
    }
}

func main() {
    TestMapSort()
}

编译执行
06go117

1.8.map反转

map的反转就是把key与value调换

package main

import (
    "fmt"
)

func Test() {
    var a map[string]int
    a = make(map[string]int,5)
    var b map[int]string
    b = make(map[int]string)
    
    a["abc"] = 100
    a["def"] = 999
    
    for k,v := range a {
        b[v] = k
    }
    
    fmt.Println(a)
    fmt.Println(b)
}

func main() {
    Test()
}

编译执行
06go118

2.包

go官方提供了150多个标准库,还可以在github上找第三方的包

2.1.线程同步

首先先理解一个概念,为什么会有锁这个概念,同一个程序可能会有多个线程要访问,如果不加限制会导致竞争,这样会导致程序崩溃,比如火车上厕所只能允许一个人进去完了下一个人进去,但这个厕所是共享出来的,通过锁来限制对其访问。

下面以一个goroute的例子来展示互斥锁与读写锁

package main

import (
    "fmt"
    "math/rand"
    "sync"
)


func TestMap() {
    var a map[int]int
    a = make(map[int]int,5)
    a[1] = 1
    a[2] = 2
    a[99] = 100
    a[44] ==990
    a[4] = 10
    for i := 0;i < 2;i++ {
        go func(b map[int]int) {
            b[4] = rand.Intn(100)
        }(a)
    }
    fmt.Pring(a)
}

func main() {
    TestMap()
}

互斥锁
只允许一个线程访问,访问完了下一个线程才能访问
对上面的程序进行编译执行
go build --race -o bin\example30..exe go_dev\day4\example30
example30.exe
报错
07go120
注意在编译的时候需要加上--race,这样才会有效检测是否存在竞争

如何解决这种情况呢?加一个互斥锁

package main 

import (
    "fmt"
    "rand/math"
    "sync"
)

var lock sync.Mutex
func TestMap() {
    var a map[int]int
    a = make(map[int]int,5)
    for i := 0;i < 2; i++ {
        go func(b map[int]int) {
            lock.Lock()
            b[4] = rand.Intn(100)
            lock.Unlock()
        }(a)
    }
    lock.Lock()
    fmt.Println(a)
    lock.Unlock()
}

func main() {
    TestMap()
}

编译执行
07go121

读写锁
读写锁不对协程做限制,都可以读,但是写被限制

package main

import (
    "fmt"
    "sync"
    "time"
    "math/rand"
    "sync/atomic"
)

var rwlock sync RW.Mutex
func TestMap() {
    var a map[int]int
    a = make(map[int]int,5)
    var count int32
    a[1] = 1
    a[2] = 2
    a[3] = 3
    a[4] = 4
    a[5] = 5
   // for i := 0;i < 2;i++ {
       // go func(b map[int]int) {
           // rwlock.lock()
           // b[4] = rand.Intn(100)
         //   time.Sleep(time.Millsecond * 10)
       //     rwlock.Unlock()
     //    }
   // }
    
    // 启动100个并行的goroute,并每隔3s输出一次总的读写次数
    for i := 0;i < 100;i++ {
        go func(b map[int]int) {
            for {
                rwlock.RLock()
                time.Sleep(time.Millsecond)
                rw.RUnlock()
                atomic.AddInt32(&count,1)
            }
        }(a)
    }
    time.Sleep(time.Second * 3)
    fmt.Println(atomic.LoadInt32(&count))
}

func main() {
   TestMap()   
}

编译执行
07go122

可以来个对比,看互斥锁在同样时间下能有多少读写次数


package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
    "sync/atomic"
)

var lock sync.Mutex
func TestMap() {
    var a map[int]int
    a = make(map[int]int,5)
    var count int32
    a[1] = 1
    a[2] = 2
    a[3] = 3
    a[4] = 4
    a[5] = 5
    for i := 0;i < 100;i++ {
        go func(b map[int]int) {
            lock.Lock()
            time.Sleep(time.Millsecond)
            lock.Unlock()
            atomic.AddIntn32(&count,1)
        }
    }(a)
    time.Sleep(time.second * 3)
    fmt.Println(atomic.LoadInt32(&count))
}

func main() {
    TestMap()
}

编译执行
07go123

通过以上的比较,可以发现读写锁比互斥锁的读写次数多达100倍,所以当业务场景是读多写少可以使用读写锁