Golang 基础

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

log.Fatal 会直接中断当前服务, 即使是用 go func(){log.Fatal("end")}() 也会中断整个服务

2. goroutine

并发线程有3种模型(用户空间 - 系统空间):

  1. 内核级线程模型 1(系统线程-轻量级线程):1(用户线程) context switch 性能低
  2. 用户级线程模型 1(系统线程-轻量级线程):N(用户线程) context switch 成本低(实际由于无法有效利用多处理器, 效率低)
  3. 混合型线程模型 N(系统线程-轻量级线程):M(用户线程) golang的goroutine(coroutine)语义实现了此模型

3. channel

FIFO(管道先进先出)

for v := range c {} 这段代码, 即使c已经没有数据进入, 也不会中断, 只有当使用 close(c) 主动关闭 channel, 才会中断这个死循环

for { select {case data, flag := <- m.c} } 这段代码主动close(c), 会让返回的flag=false, 为了避免死循环, 应该主动的作出动作. 而整个for循环要退出, 最好做一个退出的event channel作为monitor, select本身随机的从多个case里面挑选一个执行

q := make(chan string) 默认只有0个空间的chan(串行)

如果不启动一个go func(){}(<-q)去消费, 直接使用q<-"s", 会导致进程被block(类似于没有打开任何通道, 没有办法进入数据), 编译器有时能检测到没有任何的goroutine, 导致deadlock

此种类型保证数据被消费后, 再继续执行

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

这时允许有一个buffer的空间不导致block

此方法允许存在一定数据buffer

没有 q<-"s" 直接使用 <-q 也会 block, 因此建议使用 go func(){}()实现协同编程

4. init()

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

init() 可以在同一個 package 內宣定义多次

init() 執行的時機會是在執行 main func 之前

5. 指针变量

*demo.Name = "ss" 是会直接修改指针指向对象的值

// 骚气的传递
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后触发一次)

//初始化定时器
t := time.NewTimer(2 * time.Second)
//当前时间
now := time.Now()
fmt.Printf("Now time : %v.\n", now)

expire := <- t.C // t.C 阻塞监听定时器事件, 只会触发一次
fmt.Printf("Expiration time: %v.\n", expire)

断路器(每隔n后触发一次)

//初始化断续器,间隔2s
var ticker *time.Ticker = time.NewTicker(1 * time.Second)

go func() {
    // ticker.C 阻塞监听定时器事件, 隔一段时间触发一次
    for t := range ticker.C {
        fmt.Println("Tick at", t)
    }
}()

time.Sleep(time.Second * 5)   //阻塞,则执行次数为sleep的休眠时间/ticker的时间
ticker.Stop()
fmt.Println("Ticker stopped")

7. runtime.LockOSThread()

golang的scheduler可以理解为公平协作调度和抢占的综合体,他不支持优先级调度。当你开了几十万个goroutine,并且大多数协程已经在runq等待调度了, 那么如果你有一个重要的周期性的协程需要优先执行该怎么办?

使用runtime.LockOSThread() 该方法的作用是可以让当前协程绑定并独立一个线程M, 子协程不会继承lockOSThread特性

可以借助runtime.LockOSThread()方法来绑定线程,绑定线程M后的好处在于,他可以由system kernel内核来调度,因为他本质是线程

tid: 当使用 goroutine, tid会新建. 当不使用 goroutine, tid会继承
pid: 使用/不使用 goroutine, pid会继承
ppid: 使用/不使用 goroutine, ppid会继承

8. defer

defer 类似于 java 中的finally, 在一个 func block 结束之前, 会自动执行

如果一个func内部有多个defer, defer是按照Stack进行存储的

// 打印是 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结束前是不会释放锁的

mutex 不具备 goroutine 的重入特性

8.1 Mutex

使用 Mutex.Lock 会使所有的协程等待这个 mutext 的释放, 实现并发编程(非常的重, 此方法会导致用户空间->系统空间context switch, 性能很差)

  • Mutex 为互斥锁,Lock() 加锁,Unlock() 解锁
  • 在一个 goroutine 获得 Mutex 后,其他 goroutine 只能等到这个goroutine 释放该 Mutex
  • 使用 Lock() 加锁后,不能再继续对其加锁,直到利用 Unlock() 解锁后才能再加锁
  • 在 Lock() 之前使用 Unlock() 会导致 panic 异常
  • 已经锁定的 Mutex 并不与特定的 goroutine 相关联,这样可以利用一个 goroutine 对其加锁,再利用其他 goroutine 对其解锁
  • 在同一个 goroutine 中的 Mutex 解锁之前再次进行加锁,会导致死锁
  • 适用于读写不确定,并且只有一个读或者写场景
// 不需要主动显示的 new一个对象, 只需要保存变量即可
var mu sync.Mutext
func hi() {
    mu.Lock()
    defer mu.Lock()
}

8.2 RWMutex

  • RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁
  • 读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁
  • 写锁会阻止其他 goroutine(无论读和写)进来,整个锁由该 goroutine 独占
  • 适用于读多写少场景

8.2.1 Lock() 和 Unlock()

  • Lock() 加写锁,Unlock() 解写锁
  • 如果在加写锁之前已经有其他的读锁和写锁,则 Lock() 会阻塞直到该锁可用,为- 确保该锁可用,已经阻塞的 Lock() 调用会从获得的锁中排除新的读取器,即写锁权限高于读锁,有写锁时优先进行写锁定
  • 在 Lock() 之前使用 Unlock() 会导致 panic 异常

8.2.2 RLock() 和 RUnlock()

  • RLock() 加读锁,RUnlock() 解读锁
  • RLock() 加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加载多个
  • RUnlock() 解读锁,RUnlock() 撤销单词 RLock() 调用,对于其他同时存在的读锁则没有效果
  • 在没有读锁的情况下调用 RUnlock() 会导致 panic 错误
  • RUnlock() 的个数不得多于 RLock(),否则会导致 panic 错误

9. WaitGroup

这个demo每隔一秒, 会输出一个数字, 所有数字输出完成后, 结束集成

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个正在处理的值, 当wg.wait时发现值不为0, 就会wait住, 当wg.done的时候,会对原子计数器做-1操作(有点类似Java中的CountDownLatch或者CyclicBarrier)

10. unsafe.Pointer

golang 内存对齐优化

Go语言是个强类型语言。也就是说Go对类型要求严格,不同类型不能进行赋值操作。指针也是具有明确类型的对象,进行严格类型检查

str := C.CString("xxx") 没有进行内存释放, 需要在后面跟上 defer C.free(unsafe.Pointer(str)) 主动释放内存

unsafe.Pointer它表示任意类型且可寻址的指针值,可以在不同的指针类型之间进行转换(类似 C 语言的 void * 的用途)

uintptr(unsafe.Pointer(z))变量表示Pointer对象所处的内存地址

  • 任何类型的指针值都可以转换为 Pointer
  • Pointer 可以转换为任何类型的指针值
  • uintptr 可以转换为 Pointer
  • Pointer 可以转换为 uintptr
u := uint32(32)
i := int32(1)
fmt.Println(&u, &i) // 打印出地址
p := &i // p 的类型是 *int32
p = &u // 不能赋值
p = (*int32)(&u) // 强转也不能
p = (*int32)(unsafe.Pointer(&u)) // 可以赋值

11. iota

常量计数器, 只能在常量的表达式中使用

// 每次 const 出现时,都会让 iota 初始化为0
const (
    OutMute AudioOutput = iota // 0
    OutMono                    // 1
    OutStereo                  // 2
    _
    _
    OutSurround                // 5
)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • 在 go 语言中提供了并发编程模型和工具外,还提供传统的同步工具锁,包括互斥锁和读写锁有关互斥锁有些内容我们必须清...
    zidea阅读 640评论 0 10
  • 一、各种语言入门例子:打印Hello,World! package main import ( "fmt" ) f...
    Boger_8cf1阅读 95评论 0 0
  • 我的妈妈,童年时没有完整的家,所以她加倍爱我;她经历了知青下乡,所以分外热爱工作。她热情、聪敏、美貌、慈爱...
    洪霞_27cd阅读 203评论 0 2
  • 今天学校又被淹了 哪儿都去不了 所以这是今天的推送hhhhhhhh
    章鱼薇阅读 127评论 0 0
  • 其实有不详的预感早就告诉我。
    马上做阅读 69评论 0 0