Golang Snippets

Golang

install

Getting Started - The Go Programming Language

首先下載得到 go1.11.2.linux-a md64.tar.gz

tar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gz

之后在 /usr/local 有個(gè) go1.11.2 的目錄

然后加入 path

export PATH=$PATH:/usr/local/goxxx/bin
export GOROOT=/usr/local/goxxx

Go都是值傳遞 不用指針函數(shù)中得到的就是一個(gè)copy

func(f F) Foo {
f.xx ...  //調(diào)用aaa.Foo函數(shù)時(shí)會(huì)產(chǎn)生aaa的一個(gè)copy
}

func(f *F) Foo {}// 這樣就不會(huì)

Array 是值傳遞 但是map slice channel 表現(xiàn)的很像是引用

First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer. (Slices and maps act as references)

https://golang.org/doc/faq#references

Error

實(shí)現(xiàn)了Error方法的struct 都是error的實(shí)現(xiàn) 很方便的用于定制error類(lèi)型

type error interface {
    Error() string
}


JSON

map to string

    fmt.Println("fill map::", rsFillmap)
    if jsonByte, err := json.Marshal(rsElemap); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("..............", string(jsonByte))
    }

string to map

    jsonStr := `
    {
        "name":"liangyongxing",
        "age":12
    }
    `
    var mapResult map[string]interface{}
    if err := json.Unmarshal([]byte(jsonStr), &mapResult); err != nil {
        t.Fatal(err)
    }

Slice

僅聲明 需要用make 但是初始化不用 所以一般用最后一種

arr := make([]int, 5)

// 實(shí)際上這里是一個(gè)slice
args := []string{"what", "ever", "you", "like"}


b := []string{}  //這樣也可以創(chuàng)建一個(gè)slice  沒(méi)有必要make

關(guān)于interface

    mmm := map[string]interface{}{}
    data := `{"key": [{"haha": {"xixi": 10}}]}`
    json.Unmarshal([]byte(data), &mmm)

    if rs, ok := mmm["key"].([]map[string]interface{}); ok {
        fmt.Println("ok", rs)
    }else{
        fmt.Println("not ok")
    }
    //not ok


    if rs, ok := mmm["key"].([]interface{}); ok {
        fmt.Println("ok", rs)
        if rs2, ok2 := rs[0].(map[string]interface{}); ok2 {
            fmt.Println("ok2", rs2)
        }

    }else{
        fmt.Println("not ok")
    }   
  //ok2 map[haha:map[xixi:10]]
  //ok [map[haha:map[xixi:10]]]

第一個(gè)if 結(jié)果是not ok 原因是go只知道key下面是一個(gè)數(shù)組 起元素都是interface{} 類(lèi)型 不能知道這里的元素是 map[string]interface{}
所以這么斷言是not ok

map init

僅聲明不賦值 需要用make
初始化的同時(shí)賦值 可以用后一種

    m := make(map[string]int)
    m["haha"] = 1
    
    mm := map[string]int{
        "Bell Labs": 1,
        "MMM":2,
    }
    fmt.Printf("%+v  %+v", m, mm)


Map range / Map iterate


for k, v := range m {
    fmt.Printf("k=%v, v=%v\n", k, v)
}

刪除某個(gè)key

var sessions = map[string] chan int{};
delete(sessions, "moo");

map to struct

go - Converting map to struct - Stack Overflow

import "github.com/mitchellh/mapstructure"

mapstructure.Decode(myData, &result)

map key exists

if _, ok := map[key]; ok {
   //存在
}

map & mutex

Map不是線程安全的 當(dāng)有多個(gè)資源同時(shí)寫(xiě)入&讀取就會(huì)有問(wèn)題,
此時(shí)往往要用到鎖 mutex
這意味著線程將有序的對(duì)同一變量進(jìn)行訪問(wèn)

Exception Handing

func Log(t interface{}) {
    defer func() {
        if p := recover(); p != nil {
            fmt.Printf("panic recover !!! p: %v", p)
            debug.PrintStack()
        }
    }()

    s := reflect.ValueOf(&t).Elem()
    typeOfT := s.Type()

    for i := 0; i < s.NumField(); i++ {
        f := s.Field(i)
        fmt.Printf("%d: %s %s = %v\n", i,
            typeOfT.Field(i).Name, f.Type(), f.Interface())
    }
}


String to Byte (byte to string)

[]byte(str)

string(bytes)

String To Int/Float


#string to int  
int,err:=strconv.Atoi(string)  
#string to int64  
int64, err := strconv.ParseInt(string, 10, 64)  

#int to string  
string:=strconv.Itoa(int)  
#int64 to string  
string:=strconv.FormatInt(int64,10)
還可以使用 Sprint 更通用 對(duì)于 float 也可以處理
fmt.Sprint(5.03)


#string到 float
iiii, _ := strconv.ParseFloat("32.23", 64)

float to int
int(float)

int to float
float(int)  float64(xxx)

[]int to string

strings.Join 只能接收 []string

strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")

:=

:= 左邊有新變量即可

#string到int  
int,err:=strconv.Atoi(string)  
#string到int64  
int64, err := strconv.ParseInt(string, 10, 64)  
#int到string  
string:=strconv.Itoa(int)  
#int64到string  
string:=strconv.FormatInt(int64,10)  

swap

nums[I], nums[j] = nums[j], nums[I]

defer

PS 即使return defer 也會(huì)執(zhí)行

func f() (result int) {

  defer func() {
    result++
  }()
  return 0
}
上面函數(shù)返回1,因?yàn)閐efer中添加了一個(gè)函數(shù),在函數(shù)返回前改變了命名返回值的值。是不是很好用呢。但是,要注意的是,如果我們的defer語(yǔ)句沒(méi)有執(zhí)行,那么defer的函數(shù)就不會(huì)添加,如果把上面的程序改成這樣:

func f() (result int) {

  return 0
  defer func() {
    result++
  }()
  return 0
}
上面的函數(shù)就返回0了,

if else 作用域

if 中聲明的變量 在同一層else中也有效


    if bytes, err := json.Marshal(bizResp); err != nil {
        errMsg := fmt.Sprintf("fail to encode biz-resp : %v", bizResp)
        log.Error(errMsg)
        return nil, errors.New(errMsg)
    } else {
        refType := reflect.TypeOf(bizResp)
        w := &clueThrift.ThriftProtocolResponseWrapper{
            BaseResp:       &base.BaseResp{},
            JsonData:       string(bytes),
            OriginTypeName: refType.PkgPath() + "/" + refType.Name(),
        }
        return w, nil
    }

[]interface 數(shù)組賦值傳參

Frequently Asked Questions (FAQ) - The Go Programming Language

當(dāng)我們把一個(gè)string的slice 試圖賦值給 一個(gè)interface的slice的時(shí)候
cannot use arr (type []string) as type []interface {}

    sss := []string{"xxx"}
    AA(sss)


func A(pp []interface{}){
    fmt.Println(pp)
}

可以這么做 (在作為參數(shù)傳遞時(shí)尤其如此) (PS 這是比較trick的做法)

  dest := []interface{}{}
    queries :=[]interface{}{}
    queries = append(queries, instanaceIds)
    dest = queries

通過(guò)反射 調(diào)用interface{} 函數(shù)

如果一個(gè)函數(shù)裝進(jìn)了interface{} 中,如何通過(guò)反射調(diào)用他呢?
如何裸寫(xiě)一個(gè)goroutine pool | Legendtkl

type worker struct {
    Func interface{}
    Args []reflect.Value
}


        wk := worker{
            Func: func(x, y int) {
                fmt.Println(x + y)
            },
            Args: []reflect.Value{reflect.ValueOf(i), reflect.ValueOf(i)},
        }


reflect.ValueOf(ch.Func).Call(ch.Args)


斷言

v := varI.(T)    //T是你要將接口轉(zhuǎn)換的類(lèi)型   // unchecked type assertion
varI 必須是一個(gè)接口變量,否則編譯器會(huì)報(bào)錯(cuò):

再看一個(gè)斷言的例子

package main

import "fmt"
import "reflect"

type Xixi struct{
    A string
}


func main() {
    test(Xixi{A:"aaa"})
    testSlice([]Xixi{Xixi{A:"aaa"}, Xixi{A:"abb"}, Xixi{A:"acc"}})
}

func test(any interface{}){
    v := reflect.ValueOf(any)
    fmt.Printf("%+v  %+v\n", v, any)  //{A:aaa}  {A:aaa} //但是此時(shí)并不能 v.A  //因?yàn)間o并不知道這究竟是哪種類(lèi)型的數(shù)據(jù)
    if realV, ok := any.(Xixi); ok {
        fmt.Printf("%+v %+v ...\n", realV, realV.A)
    }
} 

func testSlice(any interface{}){
    v := reflect.ValueOf(any) //可以將any識(shí)別出是 [] //此時(shí)就可以循環(huán)了
    for i := 0; i < v.Len(); i++ {
        fmt.Printf("%+v\n", v.Index(i))
    }   
    
    //當(dāng)然可以用斷言一步到位
    if realV, ok := any.([]Xixi); ok{
        fmt.Println(len(realV), realV[0]) //3 {aaa}
    }else{
        fmt.Println("err")
    }
}

得到變量類(lèi)型

reflect.TypeOf(x)


    b := "123"
    fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b).Kind())
  string string



var x float64 = 3.4
fmt.Println("value:", reflect.ValueOf(x).String())

將interface{} 還原成原來(lái)實(shí)參(reflect)

Range a interface that holds a slice (or map)

當(dāng)一個(gè)interface{} 里實(shí)際存著是一個(gè)slice的時(shí)候 如何range這個(gè)interface{} 呢
這里不是最好
的辦法 最好的方式是用斷言

reflect.TypeOf(t) 返回Person (也就是struct的名字 )
reflect.TypeOf(t).Kind() 返回 struct 也就是更基本的類(lèi)型

package main

import "fmt"
import "reflect"

func main() {
    data := []string{"one","two","three"}
    test(data)
    moredata := []int{1,2,3}
    test(moredata)
    ddd := make(map[int]string)
    ddd[1]= "xixi"
    test(ddd)
} 

func test(t interface{}) {
  switch reflect.TypeOf(t).Kind() {
    case reflect.Slice:
        s := reflect.ValueOf(t)

        for i := 0; i < s.Len(); i++ {
            fmt.Println(s.Index(i))
        }
      case reflect.Map:
        v := reflect.ValueOf(t)
        for _, key := range v.MapKeys() {
            strct := v.MapIndex(key)
            fmt.Println(key.Interface(), strct.Interface())
        }
    }
}


<< && >>


n << x  表示 n * (2^x). 
y >> z  表示 y / (2^z).

枚舉類(lèi)型實(shí)現(xiàn)


type ModelField int8

const (
    NULL          ModelField = iota
    AdvId
    InstanceId
    Name
    ComponentType
    CreateTime
    Status
    IsDel
    LotteryPool
    // ......
)

fmt.Print("id::", InstanceId, AdvId)  //得到 2 和 1

當(dāng)然了 ModelField = iota 也可以替換成 int = itoa
這樣寫(xiě)成 type 有個(gè)好處就是可以為這個(gè)類(lèi)型增加一個(gè)方法

作用域

如果一個(gè)type 定義在func 中 那么只有這個(gè)func 才能使用這個(gè)type 外面并不能訪問(wèn)

打印指針

    a1 := AA{}
    a2 := AA{}
    a1.A = A{Ha:"xx"}
    fmt.Printf("... %p ... %p", &a1, &a2)

修改map中的值

map里是struct等復(fù)雜的對(duì)象 是不可以被直接修改的
比如
map[key] = A{}
然后又想 map[key].xx = xx
這樣不行哦

很多時(shí)候要把map中的元素傳到另一個(gè)func 中去修改 那么就要傳指針 然而

https://github.com/golang/go/issues/11865
spec: can take the address of map[x]
也就是說(shuō)不能夠 &map[key]

那怎么辦? 干脆在創(chuàng)建map的時(shí)候就用value的指針 而不是 value

    s := make(map[string]*Student)
    s["chenchao"] = &Student{
        Name:"chenchao",
        Id:111,
    }
    s["chenchao"].Id = 222

為基礎(chǔ)類(lèi)型增加方法(自定義 Int)


type Int int

func (i Int) Add(j Int) Int {
  return i + j
}

func main() {
  i := Int(5)
  j := Int(6)
  fmt.Println(i.Add(j))
  fmt.Println(i.Add(j) + 12)
}


不定長(zhǎng)參數(shù)

函數(shù)接收一個(gè)不定長(zhǎng)參數(shù)
和ES6有些不同的是
golang中
…XXX 是將多個(gè)item 合并到一個(gè)[]
XXX… 是打散[]

但是ES6中 形參 …xxx 是將多個(gè)item合并到xxx 比如function a(…xxx){}
如果 …xxx 這樣的寫(xiě)法作為實(shí)參 就是打散 a(…xxx)

type Xixi struct{
    A string
}


func main() {
    f := func() interface{} {
        return 1
    }
    
    
    test(1, "wowo", Xixi{A: "aia"}, f)
}

func test(haha ...interface{}) {
    //haha 是一個(gè)[]  數(shù)組OR slice
    fmt.Println(reflect.TypeOf(haha)) //[]interface {}  
    fmt.Printf("%+v \n", haha) //[1 wowo 0x108f470] 
    fmt.Printf("%+v\n", reflect.ValueOf(haha[2])) //{A:aia}
    test2(haha) //得到 1  注意這樣傳下去 是把整個(gè)[]傳到下一個(gè)函數(shù)了 
    test2(haha...) //把[]打散作為多個(gè)實(shí)參 得到4  //注意這里和ES6語(yǔ)法上的不同
}
func test2(xixi ...interface{}){
    fmt.Printf("%+v\n", len(xixi))
}

注意和ES6的區(qū)別

var s = (...haha) => {
    console.log(haha)
    arr = []
    arr = arr.concat(...haha)
    console.log(arr)
    s2(...haha)
}

var s2 = (...xixi) => {
    console.log(xixi)
}

s(1,2,3)

//=======================================
var xxx = [1,2,3,4]
function vvv(a,b,c,d){
console.log(a,b,c,d)
}
vvv(...xxx) //打散


組合

組合有一點(diǎn)點(diǎn)像繼承,可以直接訪問(wèn)到父類(lèi)的成員變量

type Proto struct {
    BaseField string
}

type Zero struct {
    Proto
    ZeroFiled string
}

func main() {
    zero := Zero{
        Proto: Proto{
            BaseField: "1",
        },
        ZeroFiled: "sd",
    }
    fmt.Printf("%+v  %+v", zero.BaseField, zero.Proto.BaseField)
}

interface 多態(tài)

golang實(shí)際上是通過(guò)組合實(shí)現(xiàn)的繼承
Golang中的面向?qū)ο罄^承

struct 可以嵌套 struct
struct 可以嵌套 interface{}
interface 也可以嵌套 interface{}

interface 繼承 method-has-a-pointer-receiver 問(wèn)題


type Pet interface {
    SetName() string
}
type Dog struct {
    Name string
}

func (g *Dog) SetName() string {
    g.Name = "..."
    return "..."
}


func Test_inter(t *testing.T) {
    var p Pet
    g := Dog{}
    p = g  //這里會(huì)報(bào)錯(cuò) method has a pointer receiver
  p = &g //這是最簡(jiǎn)單的一種解決方案 
    p.SetName()
    fmt.Printf(g.Name)
}

 

參考 go - X does not implement Y (… method has a pointer receiver) - Stack Overflow
第一個(gè)回答還提到了另一個(gè)解決方案. 用另外一個(gè)結(jié)構(gòu)體包裝一下
用結(jié)構(gòu)體包裝一下 實(shí)際上還是借用指針

import "fmt"

type Stringer interface {
    String() string
}

type MyType struct {
    value string
}

func (m *MyType) String() string { return m.value }

type MyType2 struct {
    *MyType
}

func main() {
    var s Stringer
    m := MyType{value: "something"}
    // s = m // has a pointer receiver error
    m2 := MyType2{MyType: &m}
    s = m2
    fmt.Printf(s.String())
}

go testing!!

首先待測(cè)文件命名要是 xxx_test.go
需要進(jìn)入到待測(cè)文件所在目錄
運(yùn)行某個(gè)待測(cè)文件

go test -v xxx_test.go

-v 表示顯示log

運(yùn)行某一個(gè)方法

go test -count=1 -v -run Test_service_ListSites$  (Test_service_ListSites 是待測(cè)的方法的regexp表達(dá)式)
package listing

import (
    "testing"

    "github.com/stretchr/testify/assert"
)

func Test_service_ListSites(t *testing.T) {
    assert := assert.New(t)
    assert.True(true)
}

for range

  第一個(gè)參數(shù)是idx
    for _, p := range lotterInstanceData.Prize{
        p.AdvId = advId
    }

error and panic

在go里面 錯(cuò)誤和異常是不同的
錯(cuò)誤是自己手動(dòng)創(chuàng)建出來(lái)的一個(gè)類(lèi)型 異常就像其他語(yǔ)言需要try起來(lái)的部分


func funcA() error {
    defer func() {
        if p := recover(); p != nil {
            fmt.Printf("panic recover! p: %v\n", p)
            
        }
    }()
    return funcB()
}

func funcB() error {
    // simulation
    // panic("foo")
    return errors.New("!funb erroR!")
}

Err to string

err.Error() 

func test() {
    err := funcA()
    if err == nil {
        fmt.Printf("test err is nil\\n")
    } else {
        fmt.Printf("test err is %v\\n", err)
    }
}


func main() {
    test()
}

json to map

json to map 之后 數(shù)值類(lèi)型都是float64

    mmm := map[string]interface{}{}
    data := `{"key": 10}`
    json.Unmarshal([]byte(data), &mmm)
    fmt.Printf("\n %+v \n", reflect.TypeOf(mmm["key"]))
  //float64

struct簡(jiǎn)寫(xiě) property

type server struct {
    *app.App
}
相當(dāng)于
type server struct {
    App *app.App
}

在同一個(gè)目錄里有多個(gè)main函數(shù)

文件開(kāi)頭加上 // +build OMIT

dlv build

go build -o ./cmd/insight/dlv -gcflags "all=-N -l" ./cmd/insight/main.go

然后
dlv --listen=:2345 --headless=true --api-version=2 exec ./dlv
此時(shí)會(huì)等待 goland 的debug連接

點(diǎn)開(kāi)蟲(chóng)子圖標(biāo),就啟動(dòng)辣


**#!/usr/bin/env bash**
CURDIR=**$***(pwd)*
*echo*$CURDIR
*rm*./cmd/meteor-api/dlv
*go*build -o ./cmd/meteor-api/dlv -gcflags "all=-N -l" ./cmd/meteor-api/main.go
*cd***$**{CURDIR}/cmd/meteor-api/
*dlv*--listen=:2345 --headless=true --api-version=2 exec ./dlv


time format 當(dāng)前時(shí)間

fmt.Println(time.Now().Format("2006-01-02 15:04:05"))

// Y. M .D
thisMonth.AddDate(0, 0, -1).Format(DATE_FORMAT)

theTime.Unix() //轉(zhuǎn)化為時(shí)間戳 類(lèi)型是int64

// 生成時(shí)間對(duì)象
startTime, err = time.Parse("2006-01-02T15:04:05.000Z", "2019-01-18T16:00:00.000Z")

   //時(shí)間戳 to 時(shí)間
   tm := time.Unix(1531293019, 0)
   fmt.Println(tm.Format("2006-01-02 15:04:05")) //2018-07-11 15:10:19

go mod 1.11

Modules · golang/go Wiki · GitHub

默認(rèn) GO111MODULE 的值是 auto

如果你的項(xiàng)目在 go path 下 但是仍然希望使用新的包管理
需要設(shè)置

set -x GO111MODULE on

需要go mod init YOUR_MOD_NAME 新建一個(gè)go.mod

go build go get go run 等 go 命令會(huì)更新 go.mod 文件 (也是在GO111MODULE on 的情況下)

GO111MODULE on 的情況下才能使用 go mod vendor

dep

如果是 go1.11之前的 推薦使用 dep 包管理


dep ensure -add xxx@master

rand

(100) //產(chǎn)生0-100的隨機(jī)整數(shù)

匿名字段

type Human struct {
    name string
    age int
    weight int
}

 

type Student struct {
    Human  // 匿名字段,那么默認(rèn)Student就包含了Human的所有字段
    speciality string
}

匿名結(jié)構(gòu)體

json.Marshal(struct{
    Name string
    age int
}{"name",18})

func (t AAA) GetType() struct {
    IsSDK bool
} {
    return struct{
        IsSDK bool
    }{
        IsSDK: false,
    }

}


litter 方便好用的 print

import "github.com/sanity-io/litter"

var (
    Dump  = litter.Dump
    Sdump = litter.Sdump
)

函數(shù)是一等公民

type A struct {
    Count func(c int) int
}
// 這樣是表示在 A 類(lèi)型中有一個(gè) property Count 它是一個(gè)函數(shù)
// 然后這樣賦值
c := A{
    Count: func(c int) int { return 12 },
}


嵌套結(jié)構(gòu)體初始化

type Account struct {
    Id     uint32
    Name   string
    Nested struct {
        Age uint8
    }
}

//方法1  不推薦  太麻煩  而且容易出錯(cuò)
account := &Account{
        Id:   10,
        Name: "jim",
        Nested: struct {
            Age uint8
        }{
            Age: 20,
        },
    }

//方法2  推薦
acc ;= new Account()
acc.Nested.Age = 20
OR
acc := Account{}
acc.Nested.Age = 29


enum

go 沒(méi)有枚舉關(guān)鍵字 但是可以通過(guò) const + itoa 來(lái)實(shí)現(xiàn)
itoa + 1 表示從1 開(kāi)始

    type State int
    const (
        Phone State = iota + 1
        Form
        MapSearch
    )

  const (
      Haha int = 5
  )


switch


    type State int
    const (
        Phone State = iota + 1
        Form
        MapSearch
    )

    day := State(1)
    
    switch day {
    case Phone:
        fmt.Print(day)
    default:
        fmt.Print(0)
    }


    str :="5"
    switch str {
        case "5","3":   
            fmt.Print("hahah in!")
        case "2":
            fmt.Print("hahah 222!")     
    }

獲取當(dāng)前程序所在目錄

func getCurrentFilePath() string {

    dir, err := os.Getwd()
    if err != nil {
        logs.Debug("current file path err %+v", err)
    }
    fmt.Printf("current dir : %+v", dir)

    return dir
}


channel

go語(yǔ)言之行—golang核武器goroutine調(diào)度原理、channel詳解 - W-D - 博客園
Goroutine本質(zhì)上是協(xié)程,可以理解為不受內(nèi)核調(diào)度,而受go調(diào)度器管理的線程。
協(xié)程與線程主要區(qū)別是它將不再被內(nèi)核調(diào)度,而是交給了程序自己而線程是將自己交給內(nèi)核調(diào)度,所以也不難理解golang中調(diào)度器的存在

golang協(xié)程——通道channel阻塞 - Go語(yǔ)言中文網(wǎng) - Golang中文社區(qū)
并發(fā)的存在,就涉及到線程通信。在當(dāng)下的開(kāi)發(fā)語(yǔ)言中,線程通訊主要有兩種,共享內(nèi)存與消息傳遞。
golang對(duì)并發(fā)的處理采用了協(xié)程的技術(shù)。golang的goroutine就是協(xié)程的實(shí)現(xiàn)。協(xié)程的概念很早就有,簡(jiǎn)單的理解為輕量級(jí)線程。
goroutine就是為了解決并發(fā)任務(wù)間的通信而設(shè)計(jì)的
golang解決方案是消息傳遞機(jī)制,消息的傳遞通過(guò)channel來(lái)實(shí)現(xiàn)

CSP : Communicating Sequential Process 的簡(jiǎn)稱(chēng), 是一種并發(fā)編程模型,由 Tony Hoare 于 1977 年提出

深入理解 Go Channel | Legendtkl

Example1

Channel 一般是用在協(xié)程之間OR和主線程通信用的 一般不會(huì)再同一個(gè)線程中寫(xiě)入和讀取,這么做會(huì)有 dead lock 報(bào)錯(cuò)
如果一定要這么做,那么這個(gè)channel 必須有緩沖區(qū)

package main

import (
    "fmt"
    "time"
)

func writeMessages(messages chan string) {
    time.Sleep(1000 * time.Millisecond)
    messages <- "ping"
}
func main() {

    messages := make(chan string)

    //如果整個(gè)程序中沒(méi)有給這個(gè) channel 寫(xiě)入值 會(huì)有 dead lock 報(bào)錯(cuò)
  //因?yàn)閺倪壿嬌险f(shuō)會(huì)永遠(yuǎn)卡在<-messages
    go writeMessages(messages)
    
    rs := <-messages
    fmt.Printf("rs  %+v", rs)

  close(messages)
    fmt.Println("just end")
}


Example2

寫(xiě)入和讀取發(fā)生在同一個(gè)線程O(píng)R協(xié)程,必須帶有緩沖區(qū)的 channel

messages := make(*chan*string, 2)
messages <- "buffered1"

fmt.Println(<-messages)
messages <- "buffered2"
fmt.Println(<-messages)
messages <- "buffered3"
messages <- "buffered4"
fmt.Println(<-messages)
fmt.Println(<-messages)

Example 3 range

package main                                                                                             
import (
    "fmt"
    "time"
    "strconv"
)

func makeCakeAndSend(cs chan string, count int) {
    for i := 1; i <= count; i++ {
        cakeName := "Strawberry Cake " + strconv.Itoa(i)
        time.Sleep(1 * time.Second)
        cs <- cakeName //send a strawberry cake
    }   
}

func receiveCakeAndPack(cs chan string) {
    for s := range cs {
        fmt.Println("Packing received cake: ", s)
    }
}

func main() {
    cs := make(chan string)
    go makeCakeAndSend(cs, 5)
    go receiveCakeAndPack(cs)

    //sleep for a while so that the program doesn’t exit immediately
    time.Sleep(13 * 1e9)
}

channel with direction

When using channels as function parameters, you can specify if a channel is meant to only send or receive values. This specificity increases the type-safety of the program.

package main
import "fmt"
func ping(pings chan<- string, msg string) {
    pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
    msg := <-pings
    pongs <- msg
}
func main() {
    pings := make(chan string, 1)
    pongs := make(chan string, 1)
    ping(pings, "passed message")
    pong(pings, pongs)
    fmt.Println(<-pongs)
}


Select{}

select關(guān)鍵字用于多個(gè)channel的結(jié)合

package main

import (  
    "fmt"
    "time"
)

func server1(ch chan string) {  
    time.Sleep(6 * time.Second)
    ch <- "from server1"
}
func server2(ch chan string) {  
    time.Sleep(3 * time.Second)
    ch <- "from server2"

}
func main() {  
    output1 := make(chan string)
    output2 := make(chan string)
    go server1(output1)
    go server2(output2)

      // 這里會(huì)阻塞 其中任何一個(gè) channel 可以取出值就可以放行
    // 如果同時(shí)兩個(gè) channel 都滿足 會(huì)隨機(jī)選一個(gè) channel
    // 當(dāng)你把 server1時(shí)間也設(shè)置3 有時(shí)結(jié)果是2 有時(shí)是1
    select {
    case s1 := <-output1:
        fmt.Println(s1)
    case s2 := <-output2:
        fmt.Println(s2)
    }
      fmt.Println("just end")
}

關(guān)于select case 中的 default

    messages := make(chan string)

    select {
    case messages<-"箱":
        fmt.Printf("111")
    default:
        fmt.Printf("xxxx")
    }

這里完全沒(méi)有開(kāi)啟任何一個(gè)協(xié)程就在向這個(gè) channel 寫(xiě)數(shù)據(jù)
按理說(shuō)會(huì) dead lock 但是因?yàn)橛?default 語(yǔ)句 雖然第一個(gè) messages 的 case 無(wú)法執(zhí)行 但是有 default 所以輸出 xxx

empty select

Block forever


package main

import (
    "fmt"
    "time"
)

func backgroundTask() {
    ticker := time.NewTicker(1 * time.Second)
    for _ = range ticker.C {
        fmt.Println("Tock")
    }
}

func main() {
    fmt.Println("Go Tickers Tutorial")

    go backgroundTask()

    // This print statement will be executed before
    // the first `tock` prints in the console
    fmt.Println("The rest of my application can continue")
    // here we use an empty select{} in order to keep
    // our main function alive indefinitely as it would
    // complete before our backgroundTask has a chance
    // to execute if we didn't.
    select {}
}



Another example

package main

import (
    "fmt"
    "time"
)

func server1(ch chan string) {
    for {
        time.Sleep(1 * time.Second)
        ch <- "from server1"

    }
}


func getVal(ch chan string) {
    for {
        fmt.Printf("  %v \n", <-ch)
    }
}

func main() {
    output1 := make(chan string)
    go server1(output1)
    go getVal(output1)
    select {}

    fmt.Println("just end")
}


Ticker Channel Coroutine 協(xié)程

Go by Example: Timers and Tickers

package main

import "time"

func main() {
    timer := time.NewTimer(time.Second * 2)
    <- timer.C  //will block until has value
    println("Timer expired")
}


Ticker Channel simulates setInterval

package main

import "time"
import "fmt"
var ticker *time.Ticker 
func main() {
    ticker = time.NewTicker(time.Millisecond * 100)
    go func() {
        for {
            select {
            case rs,ok := <-ticker.C:
                fmt.Println("Tick at",ok, rs)
            }

        }
    }()
    time.Sleep(time.Millisecond * 1500)
    ticker.Stop()
    fmt.Println("Ticker stopped")
}


    timeChan := time.NewTimer(time.Second).C
    
    tickChan := time.NewTicker(time.Millisecond * 400).C
    
    doneChan := make(chan bool)
    go func() {
        time.Sleep(time.Second * 2)
        doneChan <- true
    }()
    
    for {
        select {
        case <- timeChan:
            fmt.Println("Timer expired")
        case <- tickChan:
            fmt.Println("Ticker ticked")
        case <- doneChan:
            fmt.Println("Done")
            return
      }
    }

go func() 并行


func main() {
    go func() {
        time.Sleep(1 * time.Second)
        fmt.Println("11111")
    }()
    go func() {
        time.Sleep(3 * time.Second)
        fmt.Println("3333")
    }()
}

// 注意這樣并不能得到輸出 因?yàn)橹鬟M(jìn)程執(zhí)行完了就退出了  那么兩個(gè)協(xié)程也沒(méi)了
需要這樣
func main() {
    go func() {
        time.Sleep(1 * time.Second)
        fmt.Println("11111")
    }()
    go func() {
        time.Sleep(3 * time.Second)
        fmt.Println("3333")
    }()
    time.Sleep(5 * time.Second)
    fmt.Println("55555")
}
func UnblockGet(requestUrl string) chan string {
    resultChan := make(chan string)
    go func() {
        request := httplib.Get(requestUrl)
        content, err := request.String()
        if err != nil {
            content = "" + err.Error()
        }
        resultChan <- content
    } ()
    return resultChan
}

waitGroup

Notice, the Add must go ahead of Done

// This example fetches several URLs concurrently,

// using a WaitGroup to block until all the fetches are complete.

func ExampleWaitGroup() {

    var wg sync.WaitGroup

    var urls = []string{

        "http://www.golang.org/",

        "http://www.google.com/",

        "http://www.somestupidname.com/",

    }

    for _, url := range urls {

        // Increment the WaitGroup counter.

        wg.Add(1)

        // Launch a goroutine to fetch the URL.

        go func(url string) {

            // Decrement the counter when the goroutine completes.

            defer wg.Done()

            // Fetch the URL.

            http.Get(url)

        }(url)

    }

    // Wait for all HTTP fetches to complete.

    wg.Wait()

}


if 和 表達(dá)式


      u, err := url.Parse("https://siongui.github.io/pali-chanting/zh/archives.html")
      if err != nil {
              log.Fatal(err)
      }
      parts := strings.Split(u.Hostname(), ".")
      domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
      fmt.Println(domain)

用下面的方式變量 u 只能在 if 和 else if 中使用


if u, err := url.Parse(urlstr); err != nil {
    logs.Error("url parse error ::: %+v url:::%+v", err, urlstr)
    *return*urlstr
} *else*{
    parts := strings.Split(u.Hostname(), ".")
    domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
    *return*domain
}



go regexp

    pat := `(((abc.)def.)ghi)`
    src := `abc-def-ghi abc+def+ghi`

    fmt.Println(regexp.MatchString(pat, src))
    // true <nil>

    fmt.Println(regexp.QuoteMeta(pat))

go slice join

import strings
stringFiles := strings.Join(fileSlice[:], ",")


//Back to Slice again

import strings
fileSlice := strings.Split(stringFiles, ",")

reverse slice

實(shí)際生產(chǎn)環(huán)境可以使用linq 這個(gè)庫(kù)

package main

import (
    "fmt"
    "reflect"
)

func reverse(s []interface{}) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
        s[i], s[j] = s[j], s[i]
    }
}

func reverseAny(s interface{}) {
    n := reflect.ValueOf(s).Len()
    swap := reflect.Swapper(s)
    for i, j := 0, n-1; i < j; i, j = i+1, j-1 {
        swap(i, j)
    }

}

func main() {
    s := []interface{}{1, "2", uint(3), byte(4), float64(5)}
    reverse(s)
    fmt.Println(s)
    reverseAny(s)
    fmt.Println(s)
}


還可以使用https://github.com/ahmetb/go-linq 這個(gè)庫(kù)
這個(gè)庫(kù)似乎雖然是linq 但有一些slice的功能

http req resp

方法1 不推薦 resp.Body 只能被讀一次

resp, err := http.Post(url, “application/json”, bytes.NewBuffer([]byte(sql)))
json.NewDecoder(resp.Body).Decode(&respData)

方法2 ioUtil

req, err := http.NewRequest("GET", url, nil)
req.Header.Add("X-Orange-Caller", "ad.tetris.site_server")
resp, err := client.Do(req)
body, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
logs.Debug(string(body))

buf

resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(sql)))

buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
s := buf.String()


block goroutine & play as sleep

https://blog.sgmansfield.com/2016/06/how-to-block-forever-in-go/
Other way describe in the article needs go func(){….}

<-time.After(time.Duration(math.MaxInt64))

<-time.After(time.Duration(2 * time.Second))
fmt.Printf("........")

獲取函數(shù)名

pc, _, _, _ := runtime.Caller(1)

// Retrieve a Function object this functions parent
functionObject := runtime.FuncForPC(pc)

// Regex to extract just the function name (and not the module path)
extractFnName := regexp.MustCompile(`^.*\.(.*)$`)
name := extractFnName.ReplaceAllString(functionObject.Name(), "$1")

json unmarshal 字段類(lèi)型不統(tǒng)一

https://github.com/francoispqt/gojay#decoding

package gojay

import (
    "log"
    "reflect"
    "strconv"
    "testing"
)

type user struct {
    id    int
    name  string
    email string
}

// implement gojay.UnmarshalerJSONObject
func (u *user) UnmarshalJSONObject(dec *Decoder, key string) error {
    switch key {
    case "id":
        var tmp interface{}
        var err error
        var intVal int
        err = dec.Interface(&tmp)
        if err != nil {
            return err
        }
        log.Printf(":::%+v", reflect.TypeOf(tmp).Kind())
        if reflect.TypeOf(tmp).Kind().String() == "string" {
            intVal, err = strconv.Atoi(tmp.(string))
            u.id = intVal
        } else if reflect.TypeOf(tmp).Kind().String() == "float64" {
            u.id = int(tmp.(float64))
        }
        return err
    case "name":
        return dec.String(&u.name)
    case "email":
        return dec.String(&u.email)
    }
    return nil
}
func (u *user) NKeys() int {
    return 3
}
func TestDecoderMe(t *testing.T) {
    u := &user{}
    d := []byte(`{"id":"1213","name":"gojay","email":"gojay@email.com"}`)
    err := UnmarshalJSONObject(d, u)

    if err != nil {
        log.Fatal(err)
    }
}



pprof

Profiling Go Programs - The Go Blog

先起一個(gè)9876端口的服務(wù)

  go func() {
        fmt.Println("pprof start...")
        fmt.Println(http.ListenAndServe(":9876", nil))
    }()

http://x.x.x.x:9876/debug/pprof/ 會(huì)打開(kāi)一個(gè)界面

關(guān)于goroutine的話 可以借用這個(gè)來(lái)分析

先
go tool pprof http://x.x.x.x:9876/debug/pprof/goroutine
再
web
之后會(huì)生成一個(gè)svg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容