Golang 基礎(chǔ)

1. log(github.com/sirupsen/logrus)

log.Fatal 會直接中斷當(dāng)前服務(wù), 即使是用 go func(){log.Fatal("end")}() 也會中斷整個服務(wù)

2. goroutine

并發(fā)線程有3種模型(用戶空間 - 系統(tǒng)空間):

  1. 內(nèi)核級線程模型 1(系統(tǒng)線程-輕量級線程):1(用戶線程) context switch 性能低
  2. 用戶級線程模型 1(系統(tǒng)線程-輕量級線程):N(用戶線程) context switch 成本低(實際由于無法有效利用多處理器, 效率低)
  3. 混合型線程模型 N(系統(tǒng)線程-輕量級線程):M(用戶線程) golang的goroutine(coroutine)語義實現(xiàn)了此模型

3. channel

FIFO(管道先進(jìn)先出)

for v := range c {} 這段代碼, 即使c已經(jīng)沒有數(shù)據(jù)進(jìn)入, 也不會中斷, 只有當(dāng)使用 close(c) 主動關(guān)閉 channel, 才會中斷這個死循環(huán)

for { select {case data, flag := <- m.c} } 這段代碼主動close(c), 會讓返回的flag=false, 為了避免死循環(huán), 應(yīng)該主動的作出動作. 而整個for循環(huán)要退出, 最好做一個退出的event channel作為monitor, select本身隨機(jī)的從多個case里面挑選一個執(zhí)行

q := make(chan string) 默認(rèn)只有0個空間的chan(串行)

如果不啟動一個go func(){}(<-q)去消費(fèi), 直接使用q<-"s", 會導(dǎo)致進(jìn)程被block(類似于沒有打開任何通道, 沒有辦法進(jìn)入數(shù)據(jù)), 編譯器有時能檢測到?jīng)]有任何的goroutine, 導(dǎo)致deadlock

此種類型保證數(shù)據(jù)被消費(fèi)后, 再繼續(xù)執(zhí)行

q := make(chan string, 1) 含有1個空間的chan(并行, 并非并行度越大越好)

這時允許有一個buffer的空間不導(dǎo)致block

此方法允許存在一定數(shù)據(jù)buffer

沒有 q<-"s" 直接使用 <-q 也會 block, 因此建議使用 go func(){}()實現(xiàn)協(xié)同編程

4. init()

一個Package下是不可以有重複的變數(shù)或者是函式名稱

init() 可以在同一個 package 內(nèi)宣定義多次

init() 執(zhí)行的時機(jī)會是在執(zhí)行 main func 之前

5. 指針變量

*demo.Name = "ss" 是會直接修改指針指向?qū)ο蟮闹?/p>

// 騷氣的傳遞
func (s *ShardWorker) callStub(request interface{}) (interface{}, error) {
    return s.backend.sendGeneralRequest(request)
}

func (s *ShardWorker) PingNode(request *index_rpc.PingNodeRequest) (*index_rpc.PingNodeResponse, error) {
    v, err := s.callStub(request)
    if err != nil {
        return nil, err
    }
    return v.(*index_rpc.PingNodeResponse), nil
}

6. timer(定時器)/ticker(斷路器)

定時器(定時n后觸發(fā)一次)

//初始化定時器
t := time.NewTimer(2 * time.Second)
//當(dāng)前時間
now := time.Now()
fmt.Printf("Now time : %v.\n", now)

expire := <- t.C // t.C 阻塞監(jiān)聽定時器事件, 只會觸發(fā)一次
fmt.Printf("Expiration time: %v.\n", expire)

斷路器(每隔n后觸發(fā)一次)

//初始化斷續(xù)器,間隔2s
var ticker *time.Ticker = time.NewTicker(1 * time.Second)

go func() {
    // ticker.C 阻塞監(jiān)聽定時器事件, 隔一段時間觸發(fā)一次
    for t := range ticker.C {
        fmt.Println("Tick at", t)
    }
}()

time.Sleep(time.Second * 5)   //阻塞,則執(zhí)行次數(shù)為sleep的休眠時間/ticker的時間
ticker.Stop()
fmt.Println("Ticker stopped")

7. runtime.LockOSThread()

golang的scheduler可以理解為公平協(xié)作調(diào)度和搶占的綜合體,他不支持優(yōu)先級調(diào)度棉饶。當(dāng)你開了幾十萬個goroutine,并且大多數(shù)協(xié)程已經(jīng)在runq等待調(diào)度了, 那么如果你有一個重要的周期性的協(xié)程需要優(yōu)先執(zhí)行該怎么辦栏尚?

使用runtime.LockOSThread() 該方法的作用是可以讓當(dāng)前協(xié)程綁定并獨立一個線程M, 子協(xié)程不會繼承lockOSThread特性

可以借助runtime.LockOSThread()方法來綁定線程滑绒,綁定線程M后的好處在于钦铁,他可以由system kernel內(nèi)核來調(diào)度,因為他本質(zhì)是線程

tid: 當(dāng)使用 goroutine, tid會新建. 當(dāng)不使用 goroutine, tid會繼承
pid: 使用/不使用 goroutine, pid會繼承
ppid: 使用/不使用 goroutine, ppid會繼承

8. defer

defer 類似于 java 中的finally, 在一個 func block 結(jié)束之前, 會自動執(zhí)行

如果一個func內(nèi)部有多個defer, defer是按照Stack進(jìn)行存儲的

// 打印是 s1    close s1 v2 close s1 v1
func s1() {
    print("s1\t")
    defer print("close s1 v1\t")
    defer println("close s1 v2")
}

8. Mutex & RWMutex

讀多寫少的情況下, 盡可能的使用sync.RWMutex, 在 read 操作的時候, 使用RLock(), 在 set 操作的時候, 使用Lock()

不要在一個 for { select mu.Lock() defer mu.Unlock()}, 因為這本身是一個func, 在func結(jié)束前是不會釋放鎖的

mutex 不具備 goroutine 的重入特性

8.1 Mutex

使用 Mutex.Lock 會使所有的協(xié)程等待這個 mutext 的釋放, 實現(xiàn)并發(fā)編程(非常的重, 此方法會導(dǎo)致用戶空間->系統(tǒng)空間context switch, 性能很差)

  • Mutex 為互斥鎖才漆,Lock() 加鎖牛曹,Unlock() 解鎖
  • 在一個 goroutine 獲得 Mutex 后,其他 goroutine 只能等到這個goroutine 釋放該 Mutex
  • 使用 Lock() 加鎖后醇滥,不能再繼續(xù)對其加鎖黎比,直到利用 Unlock() 解鎖后才能再加鎖
  • 在 Lock() 之前使用 Unlock() 會導(dǎo)致 panic 異常
  • 已經(jīng)鎖定的 Mutex 并不與特定的 goroutine 相關(guān)聯(lián),這樣可以利用一個 goroutine 對其加鎖鸳玩,再利用其他 goroutine 對其解鎖
  • 在同一個 goroutine 中的 Mutex 解鎖之前再次進(jìn)行加鎖阅虫,會導(dǎo)致死鎖
  • 適用于讀寫不確定,并且只有一個讀或者寫場景
// 不需要主動顯示的 new一個對象, 只需要保存變量即可
var mu sync.Mutext
func hi() {
    mu.Lock()
    defer mu.Lock()
}

8.2 RWMutex

  • RWMutex 是單寫多讀鎖不跟,該鎖可以加多個讀鎖或者一個寫鎖
  • 讀鎖占用的情況下會阻止寫颓帝,不會阻止讀,多個 goroutine 可以同時獲取讀鎖
  • 寫鎖會阻止其他 goroutine(無論讀和寫)進(jìn)來窝革,整個鎖由該 goroutine 獨占
  • 適用于讀多寫少場景

8.2.1 Lock() 和 Unlock()

  • Lock() 加寫鎖购城,Unlock() 解寫鎖
  • 如果在加寫鎖之前已經(jīng)有其他的讀鎖和寫鎖,則 Lock() 會阻塞直到該鎖可用聊闯,為- 確保該鎖可用工猜,已經(jīng)阻塞的 Lock() 調(diào)用會從獲得的鎖中排除新的讀取器,即寫鎖權(quán)限高于讀鎖菱蔬,有寫鎖時優(yōu)先進(jìn)行寫鎖定
  • 在 Lock() 之前使用 Unlock() 會導(dǎo)致 panic 異常

8.2.2 RLock() 和 RUnlock()

  • RLock() 加讀鎖,RUnlock() 解讀鎖
  • RLock() 加讀鎖時,如果存在寫鎖拴泌,則無法加讀鎖魏身;當(dāng)只有讀鎖或者沒有鎖時,可以加讀鎖蚪腐,讀鎖可以加載多個
  • RUnlock() 解讀鎖箭昵,RUnlock() 撤銷單詞 RLock() 調(diào)用,對于其他同時存在的讀鎖則沒有效果
  • 在沒有讀鎖的情況下調(diào)用 RUnlock() 會導(dǎo)致 panic 錯誤
  • RUnlock() 的個數(shù)不得多于 RLock()回季,否則會導(dǎo)致 panic 錯誤

9. WaitGroup

這個demo每隔一秒, 會輸出一個數(shù)字, 所有數(shù)字輸出完成后, 結(jié)束集成

var wg sync.WaitGroup
for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(idx int) {
        d := time.Duration(idx)
        time.Sleep(d * time.Second)
        println(idx)
        wg.Done()
    }(i)
}
wg.Wait()

wg.add代表有增加n個正在處理的值, 當(dāng)wg.wait時發(fā)現(xiàn)值不為0, 就會wait住, 當(dāng)wg.done的時候,會對原子計數(shù)器做-1操作(有點類似Java中的CountDownLatch或者CyclicBarrier)

10. unsafe.Pointer

golang 內(nèi)存對齊優(yōu)化

Go語言是個強(qiáng)類型語言家制。也就是說Go對類型要求嚴(yán)格,不同類型不能進(jìn)行賦值操作泡一。指針也是具有明確類型的對象颤殴,進(jìn)行嚴(yán)格類型檢查

str := C.CString("xxx") 沒有進(jìn)行內(nèi)存釋放, 需要在后面跟上 defer C.free(unsafe.Pointer(str)) 主動釋放內(nèi)存

unsafe.Pointer它表示任意類型且可尋址的指針值,可以在不同的指針類型之間進(jìn)行轉(zhuǎn)換(類似 C 語言的 void * 的用途)

uintptr(unsafe.Pointer(z))變量表示Pointer對象所處的內(nèi)存地址

  • 任何類型的指針值都可以轉(zhuǎn)換為 Pointer
  • Pointer 可以轉(zhuǎn)換為任何類型的指針值
  • uintptr 可以轉(zhuǎn)換為 Pointer
  • Pointer 可以轉(zhuǎn)換為 uintptr
u := uint32(32)
i := int32(1)
fmt.Println(&u, &i) // 打印出地址
p := &i // p 的類型是 *int32
p = &u // 不能賦值
p = (*int32)(&u) // 強(qiáng)轉(zhuǎn)也不能
p = (*int32)(unsafe.Pointer(&u)) // 可以賦值

11. iota

常量計數(shù)器, 只能在常量的表達(dá)式中使用

// 每次 const 出現(xiàn)時鼻忠,都會讓 iota 初始化為0
const (
    OutMute AudioOutput = iota // 0
    OutMono                    // 1
    OutStereo                  // 2
    _
    _
    OutSurround                // 5
)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涵但,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帖蔓,更是在濱河造成了極大的恐慌矮瘟,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塑娇,死亡現(xiàn)場離奇詭異澈侠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)埋酬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門埋涧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奇瘦,你說我怎么就攤上這事棘催。” “怎么了耳标?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵醇坝,是天一觀的道長。 經(jīng)常有香客問我次坡,道長呼猪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任砸琅,我火速辦了婚禮宋距,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘症脂。我一直安慰自己谚赎,他們只是感情好淫僻,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著壶唤,像睡著了一般雳灵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闸盔,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天悯辙,我揣著相機(jī)與錄音,去河邊找鬼迎吵。 笑死躲撰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的击费。 我是一名探鬼主播拢蛋,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼荡灾!你這毒婦竟也來了瓤狐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤批幌,失蹤者是張志新(化名)和其女友劉穎础锐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荧缘,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡皆警,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了截粗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片信姓。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绸罗,靈堂內(nèi)的尸體忽然破棺而出意推,到底是詐尸還是另有隱情,我是刑警寧澤珊蟀,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布菊值,位于F島的核電站,受9級特大地震影響育灸,放射性物質(zhì)發(fā)生泄漏腻窒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一磅崭、第九天 我趴在偏房一處隱蔽的房頂上張望儿子。 院中可真熱鬧,春花似錦砸喻、人聲如沸柔逼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卒落。三九已至羡铲,卻和暖如春蜂桶,著一層夾襖步出監(jiān)牢的瞬間儡毕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工扑媚, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留腰湾,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓疆股,卻偏偏與公主長得像费坊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子旬痹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容