Go語言學習整理

hello World

func main(){
    fmt.Print("hello world")
}

應用程序入口[注意]

  • 必須是main包:package main

  • 必須是main方法:func main()

  • 文件名不一定是main.go

  • Go中main函數不支持任何返回值遭京,通過os.Exit來返回狀態(tài)

  • Go中main函數不支持入參箍邮,在程序中通過os.Args來獲取命令行參數

測試程序編寫

  1. 源碼文件以_test結尾:xxx_test.go
  2. 測試方法名稱以Test開頭:func TestXXX(t *testing.T){...}

eg:

package test

import "testing"

func TestFirstTry(t *testing.T){
    t.Log("My first try!")
}

變量和常量

變量的定義:

var a int = 1
var b int = 2
//go語言有變量類型自動推斷能力,等價于
var a = 1
var b = 2
//等價于
var(
    a = 1
    b = 2
)
//等價于
a:=1
b:=2

go變量和其他編程語言的差異

  • 復制可以自動類型推斷
  • 在一個賦值語句中可以對多個變量進行同時賦值

eg:

//交換兩個變量的值
func TestExchange(t *testing.T){
    a:=1
    b:=2
    a,b=b,a
}

常量

go語言的常量可以對指定步長或者指定移位運算進行簡化

const (
    Monday = iota+1
    Tuesday
    Wednesday
)
const(
    Readable=1<<iota
    Writable
    Executable
)

go語言基本數據類型

bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte //alias for uint8
rune //alias for int32,repewsents a Unicode code point
float32 float64
complex64 complex128

[注意]

  • go語言不允許隱式類型轉換
  • 別名和原有類型也不能進行隱式類型轉換

指針

go語言中不支持指針運算@撩蕖!!

aPtr+=1 ×

func TestPoint(t *testing.T){
   a:=1
   aPtr:=&a
   t.Log(a,aPtr,*aPtr)
   t.Logf("%T %T",a,aPtr)
}

運算符

算數運算符

運算符 描述 示例
+ 相加 A+B輸出結果30
- 相減 A-B輸出結果為-10
* 相乘 A*B輸出結果為200
/ 相除 B/A輸出結果為2
% 求余 B%A輸出結果為0
++ 自增 A++輸出結果為11
-- 自減 A--輸出結果為9

Go語言中沒有前置的++脊僚,--

比較運算符

運算符 描述 實例
== 檢查兩個值是否相等,如果相等返回True否則返回False (A==B)為False
!= 檢查兩個值是否不相等遵绰,如果不相等返回True否則返回False (A!=B)為True
> 檢查左邊是否大于右邊值辽幌,如果是返回True,否則返回False (A>B)為False
< 檢查左邊值是否小于右邊值,如果是返回True,否則返回False (A<B)為True
>= 檢查左邊是否大于等于右邊值椿访,如果是返回True,否則返回False (A>=B)為False
<= 檢查左邊值是否小于等于右邊值乌企,如果是返回True,否則返回False (A<=B)為True

用等號比較數組

  • 相同維數且含有相同個數的數組才可以比較
  • 每個元素都相等才相等
func TestCompareArray(t *testing.T){
   a:=[...]int{1,2,3,4}
   b:=[...]int{2,3,4,5}
   c:=[...]int{1,2,3,4,5}
   d:=[...]int{1,2,3,4}
   t.Log(a==b)
   t.Log(a==c)//Invalid operation: a==c (mismatched types [4]int and [5]int)
   t.Log(a==d)
}
//out:
false
true

位運算符

&^按位置清零

1&^0--1
1&^1--0
0&^1--0
0&^0--0

數組和切片

聲明數組
  • 關鍵字 數組名稱 數組 ------>var arr [3]int
  • 也可以不指定數組的大小,通過初始化來指定數組的大小
    • arr3:=[...]int{1,2,3,4}
數組的遍歷
  • 傳統(tǒng)的for循環(huán)

    for i:=0;i<len(arr3);i++{
        t.Log(arr3[i])
    }
    
  • 使用關鍵字range

    for idx,e:=range arr3{
        t.Log(idx,e)
    }
    

    變量idx為數組下標成玫,e為數組中下標為idx對應的值加酵,如果不使用idx,可以使用‘_’替代哭当。

截取指定區(qū)間的元素值

a[開始索引(包含):結束索引(不包含)]

如果取前n或者后n個元素虽画,可以省略對應邊界值

arr3 := [...]int{1, 2, 3, 4}
arr3Sec := arr3[:3]//獲取前3各元素
arr3Dec := arr3[2:]//獲取數組下標從2往后所有的元素
arr3All := arr3[:]//獲取所有元素

切片

聲明切片
var s0 []int //切片的聲明不指定數組的長度    
s1:=[...]int{1,2,3,4}
內部結構

[圖片上傳失敗...(image-b91fd6-1590455011016)]

go語言中的每一個數組都有一個對應的“數組頭”的數據結構,類似redis中的SDS荣病。

“數組頭”包含數組的元素個數以及數組的容量,當當前指針指向的數組的容量不能滿足時渗柿,便會重新申請一塊長度為原數組長度兩倍的容量个盆,并將原數組的內容拷貝到新數組中。

當我們使用切片得到的新數組系統(tǒng)并不會給我們分配一塊新的內存朵栖,只是給了我們數組指定地址的引用颊亮。所以如果一個數組有多個切片,更新其中一個數組的內容陨溅,其他的數組也會受影響终惑。

【如圖】

[圖片上傳失敗...(image-8ff4fa-1590455011016)]

切片VS數組

  • 切片通過指針引用數組的形式,使得切片的長度可以伸縮
  • 數組之間可以進行比較门扇,切片不能進行比較

Map

聲明map

m:=map[string]int{"one":1,"two":2,"three":3}
m1:=map[string]int{}
m2:=make(map[string]int,10)

在go語言中雹有,獲取通過key獲取map中指定的value時偿渡,返回兩個返回值,第一個為value,另一個為bool類型霸奕,如果key存在溜宽,返回true,如果key不存在返回false。

在go語言中质帅,如果訪問的key不存在适揉,go語言返回0,所以我們無法通過返回值是否為nil確定key是否存在。但是煤惩,我們可以通過獲取key時返回的另一個返回值確定key是否存在嫉嘀。

func TestKey(t *testing.T) {
   m := map[int]int{}
   if v, ok := m[23]; ok {
      t.Log("Key is exist",v)
   }else {
      t.Log("Key is not exist")
   }
}

遍歷map

func TestTravelMap(t *testing.T) {
   m1:=map[int]int{1:2,2:3,4:6,5:6}
   for k,v :=range m1{
      t.Log(k,v)
   }
}

map的value還可以存儲方法

func TestMapWithFunValue(t *testing.T) {
   m:=map[int]func(op int)int{}
   m[1]= func(op int) int {
      return op*op
   }
   t.Log(m[1](2))
}

字符串

  1. string是數據類型,不是指針或者引用類型
  2. string是只讀的byte slice,len函數可以包含它所包含的byte數
  3. string的byte數組可以存放任何數據

eg:

func TestString(t *testing.T){
   var s string

   t.Log(s)//初始化默認值為零值""

   s="hello"

   t.Log(len(s))

   //s[1]='3' //string是不可以變類型的byte slice
   s="\xE4\xB8\xA5" //可以存儲任何二進制數據

   t.Log(s)
   s="中"
   t.Log(len(s))//是byte數
   //取出byte slice數組中的指定位置的元素
   c:=[]rune(s)

   t.Logf("中 unicode %x",c[0])
   t.Logf("中 UTF8 %x",s)

}

函數

Go語言函數與其他語言的差異

  1. 函數可以返回多個值
  2. 所有的參數都是值傳遞:slice,map,channel會引起傳引用的錯覺
  3. 函數可以作為變量的值
  4. 函數可以作為參數和返回值
//定義有兩個返回值的函數
func returnMultiValues() (int, int) {
   return rand.Intn(10), rand.Intn(20)
}

func TestFn(t *testing.T) {
    //變量a,b分別用來接收返回值
   a, b := returnMultiValues()
   t.Log(a, b)
    //如果想忽略第二個返回值魄揉,使用'_'替代即可
   c,_:=returnMultiValues()
   t.Log(c)
}

可變長參數

func sum(ops ...int) int {
   ret := 0
   for _, op := range ops {
      ret += op
   }
   return ret
}

func TestVarParam(t *testing.T) {
   t.Log(sum(1,2,3,4,5))
}

延遲執(zhí)行函數

defer函數:在函數返回前執(zhí)行剪侮,一般用于回收某些資源或者釋放某些鎖等。

func Clear() {
   fmt.Println("Clear resources ~")
}
func TestDefer(t *testing.T){
   defer Clear()
   fmt.Println("Start")
}
//輸出
//Start
//Clear resources ~
//--- FAIL: TestDefer (0.00s)
//panic: err [recovered]
//  panic: err

屬性定義

go語言采用結構體來封裝’屬性‘

type Emloyee struct {
    Id   string
    Name string
    Age  int
}

行為定義

//一般使用該種定義方式什猖,采用指針的形式票彪,不會復制變量,節(jié)約內存空間
func (e *Emloyee) String() string {
    return fmt.Sprintf("ID:%s/Name:%s/Age:%d",e.Id,e.Name,e.Age)
}
//該種方式會額外復制一次變量的值不狮,造成內存浪費降铸。不推薦使用
func (e Emloyee) String() string {
    return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age)
}

實例化’對象‘

即實例化結構體

func TestCreateEmployeeObj(t *testing.T) {
    //1.按照結構體丁定義屬性的順序依次賦值
    e := Emloyee{"0", "Bob", 20}
    //通過制定屬性的形式為結構體賦值
    e1 := Emloyee{Name: "Mike", Age: 30}
    //返回指針類型
    e2 := new(Emloyee) 
    e2.Id = "2"
    e2.Name = "Rose"
    e2.Age = 20
    e.String()
    t.Log(e)
    t.Log(e1)
    t.Log(e2.Name)
}

接口

  1. 接口定義
type Programmer interface{
    WriteHelloWorld() string
}
  1. 接口實現
type GoProgrammer struct {
}

func (p *GoProgrammer) WriteHelloWorld() string {
    return "fmt.Println(\"hello World\")"
}
  1. test
func TestClient(t *testing.T) {
    var p Programmer
    p = new(GoProgrammer)
    t.Log(p.WriteHelloWorld())
}
//out:
//fmt.Println("hello World")

go語言并沒有通過向java的關鍵字implement實現改接口,只是實現的函數和接口中的函數簽名保持一致摇零,就認為該接口中的方法被實現了推掸,稱為Duck Type

接口變量

自定義類型

自定義類型感覺像C語言中的#define一樣,可以簡化程序的書寫驻仅。

eg:

//將函數的簽名進行替換
type IntConv func(op int) int
//通過使用自定義類型谅畅,簡化函數的書寫,在一定程度上也增加了程序的可讀性
func timeSpent(inner IntConv)IntConv{
    return func(n int) int {
        start:=time.Now()
        ret:=inner(n)
        fmt.Println("time spent:",time.Since(start).Seconds())
        return ret
    }
}

”多態(tài)“

  1. 先定一個父類:
type Pet struct {
}
func (p *Pet) Speak() {
    fmt.Println("...")
}

func (p *Pet) SpeakTo(host string) {
    p.Speak()
    fmt.Println(" ", host)
}
  1. 再定義一個子類:

    該子類’假裝繼承‘了Pet父類噪服,并重寫了父類的Speak方法

type Dog struct {
    Pet
}
func (d *Dog) Speak() {
    fmt.Println("Dog~")
}
  1. test
func TestDog(t *testing.T) {
    //1.go語言不支持顯示的類型轉換毡泻,強轉試一下~
    // var p *Pet:=(*Pet)new(Dog)//這里編譯器會編譯出錯,555~ 強轉也不行
    dog := new(Dog)
    dog.SpeakTo("Tea")
}
//out:
//...
//Tea

根據輸出結果我們可以看出來粘优,Go語言不支持函數重寫仇味,函數重寫是無效的,不能向java一樣“多態(tài)”雹顺,要想重寫Pet中的方法丹墨,我們可以 使用“組合”的形式,將Pet作為Dog的成員嬉愧,然后在Dog中重寫Pet中的方法贩挣。

eg:

type Dog struct {
    p *Pet
}

func (d *Dog) Speak() {
    fmt.Println("Dog~")
}

func (d *Dog) SpeakTo(host string) {
    d.Speak()
    fmt.Println(" ", host)
}

從上面的示例中可以看出,這個“多態(tài)”其實和java中的多態(tài)是不一樣的,十分笨重王财。

??錯誤處理

1. error接口

  1. 沒有異常機制
  2. error類型實現了error接口
  3. 可以通過error.New來快速創(chuàng)建錯誤實例
type error interface{
    Error() string
}

eg:

func getError() error {
    return errors.New("I am a error ")
}

2. panic

  • panic 用于不可恢復的錯誤
  • panic推出前會執(zhí)行defer指定的內容

os.Exit與panic的區(qū)別:

  1. os.Exit退出不會調用defer指定的函數
  2. os.Exit退出時不會輸出當前調用棧信息
func TestPanicVsExit(t *testing.T){

   defer func() {
      fmt.Println("Finally")
   }()

   fmt.Println("Start")

   panic(errors.New("Something wrong !"))

   //os.Exit(-1)

}

out:

  1. 使用panic會打印堆棧信息,并且會執(zhí)行defer函數
=== RUN   TestPanicVsExit
Start
Finally
--- FAIL: TestPanicVsExit (0.00s)
panic: Something wrong ! [recovered]
    panic: Something wrong !

goroutine 20 [running]:
testing.tRunner.func1(0xc0000b0200)
    C:/Go/src/testing/testing.go:830 +0x399
panic(0x521960, 0xc000042530)
    C:/Go/src/runtime/panic.go:522 +0x1c3
command-line-arguments.TestPanicVsExit(0xc0000b0200)
    H:/go-study/src/error/error_test.go:27 +0x107
testing.tRunner(0xc0000b0200, 0x554728)
    C:/Go/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
    C:/Go/src/testing/testing.go:916 +0x361

  1. 使用os.Exit卵迂,程序正常退出,并不會執(zhí)行defer函數
=== RUN   TestPanicVsExit
Start

3. recover錯誤恢復機制

返回panic傳遞的異常信息搪搏。

func TestRecover(t *testing.T){
   defer func() {
      if err := recover(); err!=nil{
         fmt.Println("recover from ",err)
      }
   }()

   fmt.Println("Start")
   panic(errors.New("Something wrong !"))
}

out:

=== RUN   TestRecover
Start
recover from  Something wrong !
--- PASS: TestRecover (0.00s)
PASS

??項目管理

package

  1. 基本復用模塊(以首字母大寫來表明可被包外代碼訪問)
  2. 代碼的package可以和所在的目錄保持不一致
  3. 同一目錄里的Go代碼的package要保持一致
init
  1. 在main函數被執(zhí)行前狭握,所有依賴的package的init方法都會被執(zhí)行
  2. 不同包的init函數按照導入包的依賴關系決定執(zhí)行順序
  3. 不同包可以有多個init函數
  4. 包的每個源文件也可以有多個init函數,這點比較特殊

調用遠程go程序

  1. 通過go get 來獲取遠程依賴
    1. go get -u 強制從網絡更新遠程依賴
  2. 注意代碼在Github 上不要有組織形式疯溺,以適應go get
    1. 直接以代碼路徑開始不要有src

Go未解決的依賴問題

  1. 同意環(huán)境下论颅,不同項目使用的同一包的不同保本
  2. 無法管理對包的特定版本的依賴

verder路徑

Go 1.5release版本發(fā)布,vender目錄被添加到GOPATH和GOROOT之外的依賴目錄查找解決方案囱嫩。在Go1.6之前需要手動設置環(huán)境變量恃疯。

查找依賴包路徑的解決方案如下:

  1. 當前包下的wender目錄
  2. 向上級目錄查找,直到找到src的vender目錄
  3. 在GOPATH下面查找依賴包
  4. 在GOROOT目錄下查找
常用的依賴管理工具

??協(xié)程

java線程和協(xié)程比較

  1. java創(chuàng)建一個線程默認的Stack size = 1MB

    Groutine的Stack size = 2 K

  2. KSE對應關系

    java Thread 和系統(tǒng)線程比是1:1

    Groutine 是M:N

  3. java線程機制

[圖片上傳失敗...(image-4890bd-1590455011015)]

java線程和內核線程一一對應墨闲,效率很高今妄,但是如果頻繁的進行線程的切換也會導致內核線程的切換造成較大的系統(tǒng)消耗。

  1. 協(xié)程機制:

[圖片上傳失敗...(image-deb3f6-1590455011015)]

  1. 一個系統(tǒng)線程對應一個處理器processor,每個處理器后面跟著一個協(xié)程隊列鸳碧,

  2. processor依次運行隊列中的協(xié)程盾鳞,

  3. 在啟動程序的時候,會開啟一個守護線程用來計算每個processor完成的協(xié)程數量瞻离。如果一段時間后腾仅,某個processor完成的協(xié)程數量沒有發(fā)生變化,守護線程會在協(xié)程的任務棧中插入一個特殊的標記套利,當協(xié)程讀到這個標記的時候推励,就會中斷自己,并查到該隊列的隊尾肉迫,切換成別的協(xié)程進一步繼續(xù)運行验辞。

  4. 當某一個協(xié)程被系統(tǒng)中斷了,processor會將自己移到另一個可運行的系統(tǒng)線程中喊衫,繼續(xù)執(zhí)行隊列中其他的協(xié)程跌造,當中斷的協(xié)程別喚醒之后,自己會加入到processor等待隊列中族购。當協(xié)程被中斷的時候鼻听,中斷現場會被保存到當前協(xié)程的對象中,保證協(xié)程喚醒后正常繼續(xù)運行联四。

編程實現

func TestGroutine(t *testing.T){
   for i:=0;i<10;i++{
      go func(i int) {
         fmt.Println(i)
      }(i)
   }
}

out:

=== RUN   TestGroutine
2
1
9
3
4
5
6
7
8
0
--- PASS: TestGroutine (0.00s)

并發(fā)控制

func TestCounter(t *testing.T) {
   counter := 0
   for i := 0; i < 5000; i++ {
      go func() {
         counter++
      }()
   }
   time.Sleep(1 * time.Second)
   t.Logf("counter = %d", counter)
}

out:

groutine_test.go:25: counter = 4712

上一個例子中,系統(tǒng)會開啟5000個協(xié)程撑教,完成對counter計數朝墩,因為沒用考慮并發(fā)安全機制,導致計算的結果和預想的不一致。

解決方案

使用sync.Mutex{} 的Lock()和UnLock()方法

func TestCounterThreadSafe(t *testing.T) {
   mut:=sync.Mutex{}
   counter := 0
   for i := 0; i < 5000; i++ {
      go func() {
         defer func() {
            mut.Unlock()
         }()
         mut.Lock()
         counter++
      }()
   }
   //讓外面的代碼執(zhí)行的速度慢一點收苏,因為如果外面的代碼執(zhí)行完后亿卤,可能協(xié)程還沒有執(zhí)行完,導致
   //結果和預想的不一致鹿霸,程序出現問題
   time.Sleep(1 * time.Second)
   t.Logf("counter = %d", counter)
}

看著加鎖和釋放鎖的部分可能有點難受~排吴,我也難受,但是go語言的機制確實可以這樣寫:happy:

WaitGroup

解決外部執(zhí)行時間和協(xié)程執(zhí)行時間不一致的問題

func TestCounterThreadSafeWithWaitGroup(t *testing.T) {
   mut:=sync.Mutex{}
   wg:=sync.WaitGroup{}
   counter := 0
   for i := 0; i < 5000; i++ {
      wg.Add(1)
      go func() {
         defer func() {
            mut.Unlock()
         }()
         mut.Lock()
         counter++
         wg.Done()
      }()
   }
   //等待協(xié)程執(zhí)行完懦鼠,程序才退出
   wg.Wait()
   t.Logf("counter = %d", counter)
}

CSP并發(fā)機制

Communicating sequential processes

依賴通道完成兩個通信實體之間的通信钻哩。

CSP vs Actor

  • 和Actor的直接通訊不同,CSP模式則是通過Channel進行通訊的肛冶,更松耦合一些
  • Go中的channel是有容量限制的并且獨立于處理Groutine,而如Erlang,Actor模式中的mailbox容量是無限的街氢,接收進程也總是被動的處理消息。

Channel

同步channel睦袖,會阻塞等待結果的協(xié)程珊肃。

異步channel,不會阻塞等待結果的協(xié)程馅笙。

聲明方式:

同步:retch:=make(chan string)聲明一個string類型的同步channel

異步:retch:=make(chan string,1)聲明一個string類型的異步channel伦乔,并指定容量大小為1

func service() string {
   time.Sleep(time.Millisecond*500)
   return "Done"
}

func AsyncService() chan string{
   retCh:=make(chan string,1)

   go func() {
      ret:=service()
      fmt.Println("returned result.")
      retCh<-ret
      fmt.Println("service exited.")
   }()
   return retCh
}

func otherTask(){
   fmt.Println("working on something else")
   time.Sleep(time.Millisecond*1000)
   fmt.Println("Task is done.")
}
func TestAsyncService(t *testing.T){
   reCh:=AsyncService()
   otherTask()
   fmt.Println(<-reCh)
}

out:

=== RUN   TestAsyncService
working on something else
returned result.
service exited.
Task is done.
Done
--- PASS: TestAsyncService (1.00s)
PASS

多路選擇select

  1. 多渠道的選擇
select{
    case ret:=<-retch1:
        t.Log("retch1")
    case ret:=<-retch2:
        t.Log("retch2")
    default:
        t.Error("No one returned")
}
  1. 超時控制
select{
    case ret:=<-retch:
        t.Log("retch1")
    case <-time.After(time.Second*1)
        t.Error("time out")
}

channel廣播&關閉

如果一個協(xié)程向通道中寫數據,有多個協(xié)程從通道中獲取數據董习,那么消費的協(xié)程怎么知道什么時候生產數據的協(xié)程生產完畢了呢烈和?

答:使用close函數關閉協(xié)程。

  1. 向關閉的channel發(fā)送數據阱飘,會導致panic
  2. v斥杜,ok<-ch;ok為bool值,true表示正常接收沥匈,false表示通道關閉
  3. 所有的channel接受者都會在channel關閉時蔗喂,立刻從阻塞等待中返回上述OK值為false。

這個廣播機制常被利用高帖,進行向多個訂閱者發(fā)送信號缰儿,如:退出信號。

func dataProducer(ch chan int, wg *sync.WaitGroup) {
    go func() {
        for i := 0; i < 10; i++ {
            ch <- i
        }
        close(ch)
        wg.Done()
    }()
}
func dataReceiver(ch chan int, wg *sync.WaitGroup) {
    go func() {
        for {
            if data, ok := <-ch; ok {
                fmt.Println(data)
            }else {
                break
            }
        }
        wg.Done()
    }()
}
func TestCloseChannel(t *testing.T) {
    var wg sync.WaitGroup
    ch:=make(chan int)
    wg.Add(1)
    dataProducer(ch,&wg)
    wg.Add(1)
    dataReceiver(ch,&wg)
    wg.Add(1)
    dataReceiver(ch,&wg)
    wg.Wait()
}

out:

=== RUN   TestCloseChannel
1
0
3
4
5
6
7
8
9
2
--- PASS: TestCloseChannel (0.00s)
PASS

上下文任務取消

Context

  1. 根Context:通過context.Background()創(chuàng)建
  2. 子Context:context.WithCancel(parentContext)創(chuàng)建
    1. ctx,cancel:=context.WithCancel(context.BackGround())
  3. 當前Context被取消時散址,基于他的子context都會被取消
  4. 接收取消通知<-ctx.Done()
func isCancelled(ctx context.Context) bool {
   select {
   case <-ctx.Done():
      return true
   default:
      return false
   }
}

func TestCancel(t *testing.T) {
   ctx,cancel :=context.WithCancel(context.Background())
   for i := 0; i < 5; i++ {
      go func(i int, ctx context.Context) {
         for {
            if isCancelled(ctx) {
               break
            }
            time.Sleep(time.Millisecond * 5)
         }
         fmt.Println(i, "Canceled")
      }(i, ctx)
   }
   cancel()
   time.Sleep(time.Second * 1)
}

只執(zhí)行一次的代碼

使用sync.Once中的Do方法

可以使用該方法構造單例模式

type Singleton struct {

}

var singleInstance *Singleton

var once sync.Once

func GetSingletonObj() *Singleton {
   once.Do(func() {
      fmt.Println("create a Obj")
      singleInstance = new (Singleton)
   })
   return singleInstance
}

func CreateSingletonObj() *Singleton {
   return GetSingletonObj()
}

func TestCreateSingletonObj(t *testing.T){
   var wg sync.WaitGroup
   for i:=0;i<10;i++{
      wg.Add(1)
      go func() {
         obj:=CreateSingletonObj()
         fmt.Printf("%d\n",unsafe.Pointer(obj))
         wg.Done()
      }()
   }
   wg.Wait()
}

?利用通道構建對象池

type ReusableObj struct {
}

type ObjPool struct {
   bufChan chan *ReusableObj
}

func NewObjPool(numberOfObj int) *ObjPool {
   ObjPool := ObjPool{}
   ObjPool.bufChan = make(chan *ReusableObj, numberOfObj)
   for i := 0; i < numberOfObj; i++ {
      ObjPool.bufChan <- &ReusableObj{}
   }
   return &ObjPool
}

func (p *ObjPool) GetObj(timeout time.Duration) (*ReusableObj, error) {
   select {
   case ret := <-p.bufChan:
      return ret, nil
   case <-time.After(timeout):
      return nil, errors.New("timeout")
   }
}

func (p *ObjPool) ReleaseObj(obj *ReusableObj) error {
   select {
   case p.bufChan <- obj:
      return nil
   default:
      return errors.New("overflow")
   }
}

func TestObjPool(t *testing.T){
   pool:=NewObjPool(10)
   for i:=0;i<11;i++{
      if v,err:=pool.GetObj(time.Second*1); err!=nil{
         t.Error(err)
      }else{
         fmt.Printf("%T\n",v)
      }
   }
}

sync.Pool對象緩存

  1. 嘗試從私有對象獲取

  2. 私有對象不存在乖阵,嘗試從當前Processor共享池獲取

  3. 如果當前Processor共享池也是空的,那么就嘗試去其他Processor的共享池獲取

  4. 如果所有子池都是空的预麸,最后就用用戶指定的New函數產生一個新的對象返回瞪浸。

  5. 如果私有對象不存在則保存為私有對象

  6. 如果私有對象存在,放入當前Processor子池的共享池中吏祸。

sync.Pool對象生命周期
  1. GC會清除sync.pool緩存的對象
  2. 對象的緩存有效期為下一次GC之前
func TestSyncPool(t *testing.T){
   pool:=&sync.Pool{
      New: func() interface{} {
         fmt.Println("Create a new object.")
         return 100
      },
   }
   v:=pool.Get().(int)
   fmt.Println(v)
   pool.Put(3)
   v1,_:=pool.Get().(int)
   fmt.Println(v1)
}

HTTP 模塊

func main() {
   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprintf(w,"<h1>HelloWorld</h1>")
   })
   http.HandleFunc("/time", func(w http.ResponseWriter, r *http.Request) {
      t:=time.Now()
      timeStr:=fmt.Sprintf("{\"time\":\"%s\"}",t)
      w.Write([]byte(timeStr))
   })
   http.ListenAndServe(":8080",nil)
}

源碼地址https://github.com/xiao-ren-wu/go-study

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末对蒲,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌蹈矮,老刑警劉巖砰逻,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異泛鸟,居然都是意外死亡蝠咆,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門北滥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刚操,“玉大人,你說我怎么就攤上這事碑韵∩娜祝” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵祝闻,是天一觀的道長占卧。 經常有香客問我,道長联喘,這世上最難降的妖魔是什么华蜒? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮豁遭,結果婚禮上叭喜,老公的妹妹穿的比我還像新娘。我一直安慰自己蓖谢,他們只是感情好捂蕴,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闪幽,像睡著了一般啥辨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盯腌,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天溉知,我揣著相機與錄音,去河邊找鬼腕够。 笑死级乍,一個胖子當著我的面吹牛,可吹牛的內容都是我干的帚湘。 我是一名探鬼主播玫荣,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼大诸!你這毒婦竟也來了崇决?” 一聲冷哼從身側響起材诽,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎恒傻,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體建邓,經...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡盈厘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了官边。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沸手。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖注簿,靈堂內的尸體忽然破棺而出契吉,到底是詐尸還是另有隱情,我是刑警寧澤诡渴,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布捐晶,位于F島的核電站,受9級特大地震影響妄辩,放射性物質發(fā)生泄漏惑灵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一眼耀、第九天 我趴在偏房一處隱蔽的房頂上張望英支。 院中可真熱鬧,春花似錦哮伟、人聲如沸干花。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽池凄。三九已至,卻和暖如春谅辣,著一層夾襖步出監(jiān)牢的瞬間修赞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工桑阶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柏副,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓蚣录,卻偏偏與公主長得像割择,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子萎河,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353