四、golang內置函數、遞歸、閉包、數組切片和map


一、總體內容

1、內置函數、遞歸函數、閉包

2、數組和切片

3、map數據結構

4、package介紹

一、內置函數

注意:值類型用new來分配內存,引用類型用make來分配內存

1、close:主要用來關閉channel

2、len:用來求長度,比如string、array、slice、map、channel

3、new:用來分配內存,主要用來分配值類型,比如int、struct、浮點型。返回的是指針

代碼案例

package main

import(
   "fmt"
)

func main(){
   var i int
   fmt.Println(i)  //0

   j:=new(int)  //返回的是地址,也就是指針
   *j=100       //因為是指針,所以要這樣賦值
   fmt.Println(*j)   //100
}

4、make:用來分配內存,主要用來分配引用類型。比如chan、map、slice
5、append:用來追加元素到數組、slice中
package main

import(
   "fmt"
)

func main(){
   var a [] int
   a=append(a,10,20,30)
   fmt.Println(a)  //[10 20 30]
}
合並兩個slice
package main

import(
   "fmt"
)

func main(){
   var a [] int
   a=append(a,10,20,30)
   a=append(a, a...)  //后面的三個點,是展開,這個append是合並
   fmt.Println(a)  
}
注意這里的三個點的作用是展開

6、panic和recover:用來做錯誤處理

         panic可以快速定位到哪里出錯了

         捕獲異常的原因是因為上線項目之后不能夠隨便的停止,所以要捕獲

package main

import(
   "fmt"
   "time"
)

func test(){

   defer func(){
      if err:=recover();err !=nil{ //這里捕獲下面的系統的異常
         fmt.Println(err)          //這里的錯誤沒有指定哪一行出錯了。這里可以把堆棧打印出來
         //捕獲了異常之后下面可以繼續寫上報警的接口或者寫到日志里面
      }
   }()
   b:=0
   a:=100/b  //這里系統拋了一個異常,上面來捕獲
   fmt.Println(a)
   return
}


func main(){
   for {
      test()
      time.Sleep(time.Second)  //這里是休息1秒,參數是一個常量
   }

   var a [] int
   a=append(a,10,20,30)
   a=append(a, a...)
   fmt.Println(a)  //[10 20 30]
}
D:\project>go build go_dev/day4/example/example2

D:\project>example2.exe
runtime error: integer divide by zero
runtime error: integer divide by zero。

還可以自己手動的通過pinic來捕獲異常

package main

import(
   "fmt"
   "errors"
)

func initConfig()(err error){                       //這里是命名返回值
   return errors.New("init config failed")   //初始化error這個實例
}


func test(){

   err :=initConfig()
   if err !=nil{
      panic(err)     //panic手動的捕獲這個異常,這樣就能知道程序的哪里出錯了
   }
   return
}


func main(){
   test()
   var a [] int
   a=append(a,10,20,30)
   a=append(a, a...)
   fmt.Println(a)  //[10 20 30]
}
D:\project>go build go_dev/day4/example/example2

D:\project>example2.exe
panic: init config failed

goroutine 1 [running]:
main.test()
        D:/project/src/go_dev/day4/example/example2/main.go:17 +0xb2
main.main()
        D:/project/src/go_dev/day4/example/example2/main.go:24 +0x3b

  

數組:

var a [1]int  :這個是確定的長度,為1

var a [] int   :這個是切片

new和make的區別

new之后如果是slice等引用類型必須要用make初始化一下才可以用如下:

package main

import(
   "fmt"
)

func test(){
   s1:=new([] int)    //new了這個指針,必須要用make來初始化一下
   fmt.Println(s1)   //這個返回來一個指針&[]

   s2:=make([] int, 2)  //這個后面的2是指定容量
   fmt.Println(s2)   //這個返回來這個數據類型  [0 0 ]

   *s1=make([] int,5)  //因為s1是一個指針(地址),要想用這個指針必須初始化一下
   (*s1)[0]=100        //初始化之后給這個值賦值
   fmt.Println(s1)     //&[100 0 0 0 0]

   s2[0]=100
   fmt.Println(s2)    //[100 0]
}

func main(){
   test()
}

遞歸函數

一個函數調用自己,就叫做遞歸

package main
package main

import (
   "fmt"
   "time"
)

func recusive(n int){
   fmt.Println("hello")
   time.Sleep(time.Second)
   if n>10{     //這個是遞歸退出的條件
      return 
   }
   recusive(n+1)    //這個就是遞歸的自己調用自己
}

func main(){
   recusive(0)
}

例子一:計算階乘

package main

import (
   "fmt"

)

func factor(n int) int {
   if n==1{
      return 1
   }
   return factor(n-1)*n
}

func main(){
   a:=factor(5)
   fmt.Println(a)
}
D:\project>go build go_dev/day4/example/example5

D:\project>example5.exe
120
View Code

斐波那契數列

package main

import(
   "fmt"
)

func fabnaqi(n int) int{
   if n<=1 {
      return 1
   }
   return fabnaqi(n-1)+fabnaqi(n-2)
}

func main(){
   for i:=1;i<10;i++{
      fmt.Println(fabnaqi(i))
   }
}
D:\project>go build go_dev/day4/example/example6

D:\project>example6.exe
1
2
3
5
8
13
21
34
55
View Code

閉包:

閉包:是一個函數和與其相關作用域的結合體

下面的匿名函數中的變量和x綁定了

package main

import (
   "fmt"
)

//閉包
func Adder() func(int)int{    //這里定義的返回值要和下面的匿名函數一樣
   //下面的整體才是閉包
   var x int                 //默認為0
   return func(d int) int{   //這里匿名函數要和上面返回值類型是一樣
      x+=d
      return x
   }
}

func main(){
   f:=Adder()
   fmt.Println(f(1))        //這里的f就是執行Adder(),然后f(1),這個就是執行匿名函數,並且傳入參數
   fmt.Println(f(100))     //101
   fmt.Println(f(1000))    //1101

}
View Code

例2

package main

import (
   "fmt"
   "strings"
)

func makeSuffixFunc(suffix string) func (string )string{
   return func (name string) string{   //這里的閉包的環境變量是和suffix綁定的
      if strings.HasPrefix(name,suffix)==false{  
         return name+suffix
      }
      return name
   }
}

func main(){
   func1:=makeSuffixFunc(".bmp")
   func2:=makeSuffixFunc(".jpg")
   fmt.Println(func1("test"))
   fmt.Println(func2("test"))
}
View Code

數組

1、數組:是同一種數據類型的固定長度的序列

2、數組定義:var a [len]int 比如:var a [5] int   一旦定義長度就不會變了

3、長度是數組類型的一部分,因此var a[5]int和var a[10]int 是不同的類型

4、數組可以通過下標進行訪問,下標是從零開始的,最后一個元素下標是len-1

         遍歷的方法如下:

下面是遍歷數組的兩種方法

package main

import (
   "fmt"
)

func main(){
   var a[10] int

   a[0]=100

   for i:=0;i<len(a);i++{
      fmt.Println(a[i])
   }
   for _,value:=range a{
      fmt.Println(value)
   }
}
View Code

5、訪問越界,如果下標在數組合法范圍之外,則觸發越界,會panic

如下,就會在編譯的時候報錯,也就是pinic了

package main

import (
   "fmt"
)

func main(){
   var a[10] int

   j:=10
   a[0]=100
   a[j]=200

   fmt.Println(a)
}
View Code

6、數組是值類型,因此改變副本的值,不會改變本身的值

         在函數里面改變不會改變外部的值,在函數中改變的知識數組的副本

         如果要改變函數中的值,需要通過指針的方式來修改

package main

import(
   "fmt"
)

func test(arr *[5] int){
   (*arr)[0]=1000
}

func main(){
   var a [5] int
   test(&a)
   fmt.Println(a)  // [1000 0 0 0 0]

}
View Code

數組和切片:

1、練習,使用非遞歸的方式實現斐波那契數列,打印前100個數

package main

import (
   "fmt"
)

func fab(n int){
   var a[]uint64
   a=make([]uint64 ,n)

   a[0]=1
   a[1]=1

   for i:=2;i<n;i++{
      a[i]=a[i-1]+a[i-2]
   }

   for _,v:=range a{
      fmt.Println(v)
   }
}

func main(){
   fab(10)
}

  

2、數組初始化

a、var age0 [5]int =[5]int {1,2,3,4,5]

b、var age1=[5] int {1,2,3,4,5]

c、var age2=[…]int {1,2,3,4,5}

d、var str=[5]string{3:”hello word”,4:”tom”}

3、多維數組

a、var age[5][3] int  //5行 3 列,第一個行,第二個列

b、var f[2][3] int=[…][3]int {{1,2,3},{7,8,9}}

遍歷多維數組

package main

import (
   "fmt"
)

func testArry(){
   var a[2][5]int=[...][5]int{{1,2,3,4,5},{6,7,8,9,10}}  //前面是行,后面是列

   for row,v:=range a{           //首先便利梅行
      for col,v1:=range v{     //然后遍歷每列
         fmt.Printf("(%d,%d)=%d ",row,col,v1) //這里的v1是多維數組的遍歷對象
      }
      fmt.Println()
   }
}

func main(){
   testArry()
}
View Code

注意這里的切片和數組的區別:

  a、聲明的時候切片沒有長度,數組有長度在中括號中,因為切片是變長的,數組的是長度固定的,切片是引用類型,數組是值類型

1、切片:切片是數組的一個引用,因此切片是引用類型

2、切片的長度可以改變,因此切片是一個可變的數組

3、切片遍歷方式和數組一樣,可以用len()求長度

4、cap可以求出slice最大的容量,0<= len (slice)<=cap (array),其中array是slice引用的數組

5、切片的定義:var變量名[]切片類型,比如var str []string    var arr[] int

package main

import "fmt"

func testSlice(){
   var slice [] int
   var arr[5]int=[...]int{1,2,3,4,5}

   slice=arr[2:5]            //數組的切片
   fmt.Println(slice)
   fmt.Println(len(slice))  //求切片的長度
   fmt.Println(cap(slice))     //求切片的容量


   slice =slice[0:1]        //這個切片引用了另一個切片
   fmt.Println(len(slice))  //求新切片的長度
   fmt.Println(cap(slice))  //這個還是原來切片的長度
}

func main(){
   testSlice()
}
D:\project>go build go_dev/day4/example/slicea

D:\project>slicea.exe
[3 4 5]
3
3
1
3

 

方法:   切片初始化只能通過切片的方式

1、切片初始化:var slice [] int=arr[start”end].包含start到end之間的元素名不包含end

2、var slice []int=arr[0:end]可以簡寫為var slice []int=arr[:end]

3、var slice []int=arr[start:len(arr)] 可以簡寫為var slice[]int = arr[start:]

4、var slice []int=arr[0,len(arr)] 可以簡寫為var slice[]int=arr[:]

5、如果要切片最后一個元素去掉,可以這樣寫

         slice=slice[:len(slice)-1]

數組和切片定義之后必須要初始化

下面是切片的內存布局  第一個是x的切片布局指針方式指針數組,

上面的第三個切片的地址指向的是數組的第一個元素的地址,如下:

 

func testslice1(){
   var a=[10]int{1,2,3,4,5}

   b:=a[1:5]  //這是一個切片
   fmt.Printf("%p\n",b)  //打印地址0xc042084008
   fmt.Println(&a[2])            //打印地址0xc042084008
}

func main(){
   testslice1()
}
從上面地址可以看到切片地址和第一個元素的地址相同
View Code

3、通過make來創建切片

 

4、用append內置函數操作切片

slice=append(slice,10)

var a=[]int{1,2,3}

var b=[]int{4,5,6}

a=append(a,b…)   //這里注意,如果是一個切片的話,append需要后面是…

切片是引用,如果切片的append的大小超過了原來的數組的大小,這個時候就會擴容,可以通過切片和數組的第一個值的地址是否相同來比較

 

package main

import (
   "fmt"
)

func test(){
   var a [2]int=[...]int{1,2}
   s:=a[1:]

   fmt.Println(s,&a[1])


   s=append(s,10)
   s=append(s,11)


   fmt.Printf("%p\n",s)  //0xc04200e2a0
   fmt.Println(s,&a[1])         //0xc0420080d8
}

func main(){
   test()
}
View Code

上面切片的地址和數組的第一個元素的地址不同可以看到,這個切片進行了擴容的方式,新開了一塊內存和原來的內存不一樣了,這個是內部的算法決定的

 

5、切片遍歷

for index,val :range slice{

}

6、切片resize,切片之后可以再進行切片

var a=[] int{1,2,3}

b:=a[1:2]

b=b[0:3]

7、切片的拷貝

s1:=[]int{1,2,3,4}

s2:=make([]int ,10)

copy(s2,s1)

s3:=[]int{1,2,3}

s3=append(s3,s2…)

s3=append(s3,4,5,6)

8、stringhe slice

string底層就是一個byte的數組,因此,也可以進行切片操作

 

package main

import (
   "fmt"
)

func test(){
   s:="hello world"
   s1:=s[0:5]
   fmt.Println(s1) //hello
}

func main(){
   test()
}
View Code

10、如何改變string中的字符串?

string本身是不可變的,因此要改變string中字符,需要如下操作 

下面是針對沒有中文的情況下:

 

func test2(){
   str:="hello world"
   s:=[]byte(str)
   s[0]='o'
   str=string(s)
   fmt.Println(str)   // oello world
}

func main(){
   test2()
}
下面是可以針對中文的情況

func testModify(){
   str:="oello world"
   s:=[]rune(str)

   s[0]='h'
   str=string(s)
   fmt.Println(str)  //hello world
}

func main(){
   testModify()
}
View Code

11、排序和查找操作

排序操作主要在sort包中,導入就可以使用了

         import(“sort”)

sort.Ints對整數進行排序,sort.String對字符串進行排序,sort.Float64s對浮點數進行排序

sort.SearchInts(a []int,b int)從數組a中查找b,前提是a必須有序

sort.SearchFloats(a []float64 ,b float64)從數組a中查找b,前提是a必須有序

sort.SearchStrings(a []string,bstring)從數組a中查找b,請安提是a必須有序

下面是對數組進行排序

 

package main

import(
   "fmt"
   "sort"
)

func test(){
   var a=[...]int{1,3,2,21,11,14}  //因為這個是數組,所以不能直接排序
   sort.Ints(a[:])  //所以這里要針對切片進行排序,因為切片實際引用類型

   fmt.Println(a)
}

func main(){
   test()
}

/*
D:\project>go build go_dev/day4/example/example15

D:\project>example15
[1 2 3 11 14 21]
*/
下面是對字符串進行排序
func testString(){
   var a=[...]string{"abc","efg","b","A","eee"}
   sort.Strings(a[:])
   fmt.Println(a)  //[A abc b eee efg]
}

func main(){
   testString()
}
下面是對浮點型進行排序
func testFloat(){
   var a=[...]float64{1.1,9.7,2.1}
   sort.Float64s(a[:])
   fmt.Println(a)   //[1.1 2.1 9.7]
}

func main(){
   testFloat()
}
下面是排序的方法
func testIntsearch(){
   var a=[...]int{1,8,38,2,348,4}
   sort.Ints(a[:])
   index:=sort.SearchInts(a[:],2)  
   fmt.Println(index)    //得到下標為1
}

func main(){
   testIntsearch()
}
View Code

map數據結構

 

一、簡介

key-value的數據結構

a、聲明,聲明是不會分配內存的,初始化需要make

var map1 map[key type]value type

var a map[string]string

var a map[string]int

var a map[int]string

var a map[string]map[string]string

 

package main

import (
   "fmt"
)

func test(){
   var a map[string]string  //聲明map,但是這里沒有申請內存空間,所以必須初始化
   a=make(map[string]string,10)  //這里初始化,申請內存空間
   a["abc"]="efg"
   fmt.Println(a)  //map[abc:efg]
}

func main(){
   test()
}
方式二
package main

import (
   "fmt"
)

func test(){
   a:=make(map[string]string,10)  //這里聲明加上初始化,聲明然后分配內存空間
   a["abc"]="efg"
   fmt.Println(a)  //map[abc:efg]
}

func main(){
   test()
}
方法三:不推薦
package main

import (
   "fmt"
)

func test(){
   var a map[string]string=map[string]string{
      "key":"value",
   }
   //var a map[string]string  //聲明map,但是這里沒有申請內存空間,所以必須初始化
   //a:=make(map[string]string,10)  //這里初始化,申請內存空間
   a["abc"]="efg"
   fmt.Println(a)  //map[abc:efg]
}

func main(){
   test()
}
多層map嵌套

func testMap2(){
   a:=make(map[string]map[string]string,100)  //聲明
   a["key1"]=make(map[string]string)  //初始化
   a["key1"]["key2"]="abc"
   a["key1"]["key3"]="abc"
   fmt.Println(a)
}

func main(){
   testMap2()
   test()
}
View Code

map相關的操作

a[“hello”]=”world”   插入和更新

val,ok:=a[“hello”]    查找

for k,v:=range a {     遍歷

}

delete (a,”hello”)     刪除

len(a)              長度

 

func test3() {
   var a map[string]string = map[string]string{"hello": "world"}
   a = make(map[string]string, 10)
   a["hello"] = "world"  //插入和更新
   val,ok:=a["hello"]  //查找
   if ok{
      fmt.Println(val)
   }
   for k,v :=range a {   //遍歷
      fmt.Println(k,v)
   }


}

func main(){
      test3()
}
View Code

排序:

map排序,map的排序是無序的:

a、先獲取所有key,把key進行排序

b、按照排好的key,進行遍歷

 

package main

import(
   "fmt"
   "sort"
)

func test(){
   var a map[int]int                 //聲明
   a=make(map[int]int,5)            //初始化,加入內存

   a[8]=10
   a[3]=11
   a[2]=10
   a[1]=10
   a[18]=10

   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(){
   test()
}

//D:\project>go build go_dev/day4/example/example17
//
//D:\project>example17.exe
//1 10
//2 10
//3 11
//8 10
//18 10
上面思路,由於map是無序的,這里創建一個切片,然后根據切片的方法進行排序
View Code

map反轉

初始化另外一個map把key、value呼喚即可

package main

import (
   "fmt"
)

func test(){
   var a map[string] int
   var b map[int] string

   a=make(map[string]int,5)
   b=make(map[int]string,5)

   a["abc"]=1
   a["efg"]=2

   for k,v:=range a{
      b[v]=k
   }

   fmt.Println(a)  //map[abc:1 efg:2]
   fmt.Println(b)  //map[1:abc 2:efg]
}

func main(){
   test()
}
原理:這里首先聲明兩個map,並且初始化,然后進行遍歷第一個map,然后第二個map直接添加第一個map的v和k即可

1、golang中的包

         a、golang目前有150個標准的包,覆蓋了幾乎所有的基礎庫

         b、golang.org有所有包的文檔,沒事就翻翻

2、線程同步

         a、import(“sync”)

         b、互斥鎖 var mu sync.Mutex,同一時間只能有一個goroute能進去

         c、讀寫鎖,var mu sync.RWMutex

線程和協程,只有讀操作的時候不用加鎖

1、如果有寫操作,需要加鎖

2、如果有讀寫的操作,需要加鎖

3、如果只有讀的操作,不需要加鎖

編譯的時候—race可以查看是否有競爭

下面是互斥鎖的程序

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

//因為這是一個讀寫的操作,所以讀的時候也要加鎖
var lock sync.Mutex
func test(){
   var a map[int] int
   a=make(map[int]int,5)

   a[8]=10

   for i :=0;i<2;i++{
      go func(b map [int]int){
         //下面是寫的操作,所以要加鎖
         lock.Lock()                    //加鎖
         b[8]=rand.Intn(100)
         lock.Unlock()                  //解鎖
      }(a)
   }
   lock.Lock()                            //這是讀操作,因為程序中有讀寫,所以加鎖
   fmt.Println(a)
   time.Sleep(time.Second)
   lock.Unlock()                          //解鎖
}

func main(){
   test()
}
D:\project>go build --race go_dev/day4/example/packagea   這里—race是檢測是否有競爭

D:\project>packagea.exe                                   //因為有race所以下面檢測沒有競爭
map[8:81]
上面是互斥鎖
應用場景:
    寫比較多,有少量的讀。(寫多讀少)
    加鎖后,任何其他試圖再次加鎖的線程會被阻塞,直到當前進程解鎖
如果解鎖時有一個以上的線程阻塞,那么所有該鎖上的線程都被編程就緒狀態, 
第一個變為就緒狀態的線程又執行加鎖操作,那么其他的線程又會進入等待。 
在這種方式下,只有一個線程能夠訪問被互斥鎖保護的資源。
互斥鎖無論讀還是寫同一時間只有一個協程在執行
View Code

讀寫鎖:

實際就是一種特殊的自旋鎖,它把共享資源的訪問者划分為讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作

使用場景:

         寫比較少,有大量的讀 (讀多寫少) 

         寫的時候只有一個goroute去寫,但是讀的時候所有的goroute去讀

讀寫鎖有三種狀態:讀加鎖狀態、寫加鎖狀態和不加鎖狀態 
一次只有一個線程可以占有寫模式的讀寫鎖,但是多個線程可以同時占有讀模式的讀寫鎖。(這也是它能夠實現高並發的一種手段) 
當讀寫鎖在寫加鎖模式下,任何試圖對這個鎖進行加鎖的線程都會被阻塞,直到寫進程對其解鎖。 
當讀寫鎖在讀加鎖模式先,任何線程都可以對其進行讀加鎖操作,但是所有試圖進行寫加鎖操作的線程都會被阻塞,直到所有的讀線程都解鎖。 
所以讀寫鎖非常適合對數據結構讀的次數遠遠大於寫的情況

package main

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

var rwlock sync.RWMutex     //讀寫鎖的定義

func test(){
   var a map[int] int
   a=make(map[int]int,5)
   //var count int32

   a[8]=10

   for i:=0;i<2;i++{
      go func(b map[int]int){
         rwlock.Lock()
         b[8]=rand.Intn(100)
         rwlock.Unlock()
      }(a)
   }

   for i:=0;i<100;i++{
      go func(b map[int]int){ 
         rwlock.RLock()     //加讀寫鎖
         fmt.Println(a)
         rwlock.RUnlock()     //解鎖
      }(a)
   }
   time.Sleep(time.Second*3)


}

func main(){
   test()
}
View Code

原子操作:atomic 包  在sync下面

https://studygolang.com/pkgdoc

這是串行的操作

package main

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

)

var rwlock sync.RWMutex     //讀寫鎖的定義

func test(){
   var a map[int] int
   a=make(map[int]int,5)
   var count int32

   a[8]=10

   for i:=0;i<2;i++{
      go func(b map[int]int){
         rwlock.Lock()
         b[8]=rand.Intn(100)
         rwlock.Unlock()
      }(a)
   }

   for i:=0;i<100;i++{
      go func(b map[int]int){
         for {
            rwlock.RLock() //加讀寫鎖
            fmt.Println(a)
            rwlock.RUnlock()           //解鎖
            atomic.AddInt32(&count, 1) //原子操作進行計數 
         }
      }(a)
   }
   time.Sleep(time.Second*3)
   fmt.Println(atomic.LoadInt32(&count))   //原子操作進行讀出來529500 次


}

func main(){
   test()
}
View Code

比較讀寫鎖和互斥鎖的性能

下面是讀寫鎖
package main

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

)

var rwlock sync.RWMutex     //讀寫鎖的定義

func test(){
   var a map[int] int
   a=make(map[int]int,5)
   var count int32

   a[8]=10

   for i:=0;i<2;i++{
      go func(b map[int]int){
         rwlock.Lock()
         b[8]=rand.Intn(100)
         time.Sleep(time.Microsecond*10)
         rwlock.Unlock()
      }(a)
   }

   for i:=0;i<100;i++{
      go func(b map[int]int){
         for {
            rwlock.RLock() //加讀寫鎖
            time.Sleep(time.Microsecond)
            rwlock.RUnlock()           //解鎖
            atomic.AddInt32(&count, 1) //原子操作進行計數
         }
      }(a)
   }
   time.Sleep(time.Second*3)
   fmt.Println(atomic.LoadInt32(&count))   //251196


}

func main(){
   test()
}
下面是互斥鎖
package main

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

var lock sync.Mutex     //讀寫鎖的定義

func test(){
   var a map[int] int
   a=make(map[int]int,5)
   var count int32

   a[8]=10

   for i:=0;i<2;i++{
      go func(b map[int]int){
         lock.Lock()
         b[8]=rand.Intn(100)
         time.Sleep(time.Microsecond*10)
         lock.Unlock()
      }(a)
   }

   for i:=0;i<100;i++{
      go func(b map[int]int){
         for {
            lock.Lock()
            //rwlock.RLock() //加讀寫鎖
            time.Sleep(time.Microsecond)
            //rwlock.RUnlock()           //解鎖
            lock.Unlock()
            atomic.AddInt32(&count, 1) //原子操作進行計數
         }
      }(a)
   }
   time.Sleep(time.Second*3)
   fmt.Println(atomic.LoadInt32(&count))   //2460次
}

func main(){
   test()
}
可以看到讀寫鎖的性能是互斥鎖的性能的100倍!

讀寫鎖:讀的時候可以有多個協程在操作
互斥鎖:無論讀還是寫,只有一個協程在執行
View Code

go get安裝第三方包

go get后面跟上github地址

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM