Golang我爱编程go学习

golang基础介绍part-2(interface,gorou

2018-06-03  本文已影响178人  太白菜Rennbon

Golang相关其他分享

golang基础介绍part-1

衔接介绍

part-1中提到了Golang中各种关键字的基本用法和一些指针的注意事项,这张主要侧重go的一些相对比较高级的操作,如果对part-1的概念点还是一知半懂的话,这一章会比较困难,所以基础很重要,当然这章其实也是基础。

推荐书籍

《go in action》,《深入解析go》,《go源码剖析》,《go语言圣经》

有的书可能老了,源码已经迭代了好几版了,需要自我鉴别,目前网上也有很多分析的例子

本章概要

戳一下看demo👈

1. interface 实践 👈

methed receivers values
(t T) T and *T
(t *T) *T

如上图所示,如果使用指针接收者(*T)来实现一个接口,那么只有指向那个类型的指针(*T)才能够实现对应的接口。如果使用值接收者(T)来实现一个接口,那么那个类型的值和指针(T and *T)都能够实现对应的接口。

在go中是没有继承的概念的,所以interface和组合对象这2个概念是很重要的,demo中也就该2点做了演示

2. exception 异常与捕捉(try catch? panic defer recover) 👈

package main
import "log"
func main() {
    log.Println("service start")
    //unforgivableSystemException()
    protect(unforgivableSystemException)
    log.Println("service stop")
}
func unforgivableSystemException() {
    panic("WTF!!!!")
}
func protect(g func()) {
    //执行完g()后执行
    defer func() {
        log.Println("done")
        //如果有panic的话会被recover到,相当于C#中的catch,阻止系统直接抛异常
        if err := recover(); err != nil {
            //为什么我这里没用fmt打印,而用log,你懂得,可以异常数据收集
            log.Printf("run time panic: %v", err)
        }
    }()
    //执行对应的函数
    g()
}

相对于C#,Java或其他语言中的exception而言,在go中有error和panic,日常C#开发中,我们会把人为能识别的异常通过标记一个枚举的errorCode返回,而正真的系统级异常则是exception。类似的,在golang中就大致相对着error和panic,error能更全面的控制各种人为识别的错误,在go中不推荐bool作为执行成功与否的返回值,因为bool不能完全解释false的原由到底是什么,只能表示逻辑通不过,所以error和errorCode相对应,而panic和exception相对应。

3. goroutine and channel 👈

goroutine运行机制

图中,M是线程,P是处理器,G是goroutine,图左侧部分是M0线程上挂载了P对G队列进行处理,在执行G0时,当G0阻塞时,调度器会将M0线程与P隔离,并创建一个新的线程如图为M1来运行P上的其他G当,当阻塞在syscall上的G完成syscall调用后,G会去尝试获取一个可用的P,如果没有可用的P,那么G会被标记为runnable,之前的那个sleep的M将再次进入sleep,G在创建的时候相关执行参数会被拷贝到G的栈空间,这使得它和当前任务不再有任何关系,各自使用独立的栈空间,所以G相对于系统级多线程的上下文切换更轻量,效果更好。(这里的调度器是golang自己实现的调度器,非操作系统调度器)

channel运行机制

channel缓冲的概念如上图所示,原理上channel 其实就是一个队列加一个锁,channel中维护着队列中数据的个数,数据的大小,是否关闭状态,还配有轻量级的锁,源数据类型,收发数组的指针等等。

4. data race 和 互斥锁 以及 原子函数 👈

以下代码文件名:main.go

package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
var (
    num   int32
    num2  int32
    num3  int32
    wg    sync.WaitGroup
    mutex sync.Mutex //互斥锁
)
func main() {
    wg.Add(2)
    go Inc()
    go Inc()
    wg.Wait()
    fmt.Println(num, ":", num2, ":", num3)
}
func Inc() {
    defer wg.Done()
    for i := 0; i < 10000; i++ {
        //num3++
        /*原子函数,单纯效率比互斥锁高,
        但是原子锁由底层硬件支持,脱离的golang机制,
        虽然效率很好,但是会因为各种硬件的问题变的???(重要的字符要打3遍)
        所欲对于严格要求一致的地方不适合原子函数
        */
        atomic.AddInt32(&num, 1)

        mutex.Lock()
        { //这对花括号只是为了标记作用域,可以不写
            num2++
        }
        mutex.Unlock()
    }
}

data race 的出现会造成数据的不确定性,对于一个系统而言,这种不稳定因素是要极力避免的,以上的代码,我们通过“go run -race main.go” 出现以下的运行结果

==================
WARNING: DATA RACE
Read at 0x0000011d9e74 by goroutine 7:
  main.Inc()
      /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:27 +0x79

Previous write at 0x0000011d9e74 by goroutine 6:
  main.Inc()
      /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:27 +0x96

Goroutine 7 (running) created at:
  main.main()
      /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:20 +0x88

Goroutine 6 (running) created at:
  main.main()
      /Users/zhubin/go/src/demo/golang2/goroutine/go6/main.go:19 +0x70
==================
20000 : 20000 : 13507
Found 1 data race(s)
exit status 66

而当注释掉num3++后,运行结果将会是20000:20000:0,这里就不做细致的讲解了

上一篇下一篇

猜你喜欢

热点阅读