golang学习

前言

因为工作中主要用golang进行开发,所以最近开始学习golang。不得不说,golang是一种灵活简洁的语言,不仅吸取了很多语言的优点,原生支持的goroutine和channel更是极大简化了并发开发。

也是因为这样,初学leaf框架的时候,遇到了很多问题。虽然在看的过程中遇到不懂的再学也可以慢慢理解框架,但是那样终究还是效率太低,而且理解的很片面,因此我决定系统的学习一下golang的特性,作为我第一篇周记。

golang特性

1.指针

golang的指针与C的指针用法大致相同,不过有几个地方需要注意。

  • golang取消了->,也就是说对于一个对象的元素x,无论p是指针变量还是元素本身,都可以用p.x取到。
  • 也是因为这个原因,对于数组p[],&p的值是这个数组的地址,而不是p[0]的地址。

2.函数参数及返回值

golang的函数支持多返回值,不必采用在调用参数中添加指针来获取返回值的方式,让代码可读性更高。golang的结构体函数不是定义在结构体里面,而是单独定义在外面,和普通的函数一样,不过在函数名字前面加上结构体的声明就可以了,类似

type A struct{
    name string
    age int
}
func (obj *A) Hello() (string, int) {
    fmt.Println("Hello I'm " + obj.name)
    return obj.name, obj.age
}

这样就为A这个结构体定义了一个名为Hello的方法,调用的对象作为指针obj传进函数,返回调用对象的两个属性。

3.面向对象

golang在语言层面并没有显示的支持面向对象,但是确实有方法可以实现。

  • 封装在golang中是以包为单位的,首字母大写的函数和变量才能被其他包访问。结构体中的属性小写在json解析的时候不能获取,可以使用json:"keyName" 这种方式生成key值小写的json串。
type A struct{
    a string `json:"A"`
    B string
    c string
    D string `json:"d"`
}

func main() {
    obj := A{"a", "b", "c", "d"}
    m_json,_:= json.Marshal(obj)
    fmt.Println(string(m_json))
}

这样输出的就是 {"B":"b","d":"d"}

  • 继承可以用一种叫做组合的方式实现。组合即在一个结构体Child中声明另一个结构体Father,这样Child就可以直接使用father中的所有方法和属性。
type father struct {
Name string
FamilyName string
}
type mother struct {
Name string
}
type son struct{
father
mother
Name string
}
func main() {
    son := son{father{"name1", "family" }, mother{"name2"}, "name3"}
    fmt.Println(son.FamilyName)
}

对于继承的属性,子类可以直接调用,即son.FamilyName直接使用father的属性。但是如果声明的多个结构体中出现同名的属性或方法,就需要在调用的时候指定使用哪个结构体了。可以使用son.father.Name来指定到底调用的是father中定义的Name。

  • 重载不能显性的支持,但是可以使用 args ...interface{} 作为函数参数来支持任意多个参数,其中的interface{}可以代表任意的类型。

4.Goroutine

协程可以说是golang最大的特点了。处理高并发的任务,多线程的缺点在于切换的开销太大,而异步的回调机制又比较繁琐,要考虑各种异常情况,容易出问题。协程兼顾二者的有点,提高了程序的开发效率和运行效率。

下面是在网上找的一张图,基本说明了goroutine的结构。

协程.jpg

M:代表真正的内核OS线程,真正干活的人
G:代表一个goroutine,它有自己的栈,instruction pointer和其他信息(正在等待的channel等等),用于调度。
P:代表调度的上下文,可以把它看做一个局部的调度器,使go代码在一个线程上跑

协程可以理解为用户创建的线程,不过他们的调度是在应用层完成的。golang维护了一个线程池,runtime.GOMAXPROCS(n)可以设置池子的大小,默认只有一个,每个线程都维护自己的一个goroutine队列。golang封装了一些异步的系统接口,这样一旦一个正在运行的协程调用了这些系统接口,golang就会把这个协程打包丢到队列里面排队,然后从队列里拿到一个新的goroutine运行。
golang的协程实现非常方便,只需要go funcName()就完成了一个协程的创建过程。但是要注意的是,和创建线程不同,创建的协程会随着主线程结束而结束。

5.Channel和sync

既然有了goroutine来做高并发,自然少不了并发的通信。

  • WaitGroup
    WaitGroup总共有三个方法:
    Add:添加或者减少等待goroutine的数量
    Done:相当于Add(-1)
    Wait:执行阻塞,直到所有的WaitGroup数量变成0
var waitgroup sync.WaitGroup
func Solve(event int) {
    fmt.Println(event)
    waitgroup.Done()
}

func main() {
    for i := 0; i < 10; i++ {
        waitgroup.Add(1) 
        go Solve(i)
    }
    waitgroup.Wait()
}

这个WaitGroup可以用来确保goroutine都执行完成或者对某个资源进行监控,很像操作系统里的信号量。

  • channel
    channel是golang中一种内置类型,channel一共有4中操作:
    make:创建channel,第二个参数为缓存的大小,缺省值为0
    channel<- :向channel中存入数据
    <-channel:从channel中取出数据
    close:关闭channel
func DoSth(ch chan int) {
    fmt.Println("finish")
    <-ch
}

func main() {
    ch := make(chan int)
    go DoSth(ch)
    ch <- 1
}

首先创建一个channel,缓存大小为0即无缓存,创建goroutine,之后主协程调用ch<-向channel中存入数据,等待创建的协程执行<-ch读取。
这里说一下缓存的作用,在存入数据的时候,如果有未满的缓存,则只需要阻塞直到数据存入缓存就结束了,但是如果缓存满了,则需要等待数据被读取。

因为channel的这种阻塞特性,有可能产生死锁,我们必须处理超时的情况。

go func(){
    DoSth()
    c2 <- "Finish"
}
go func() {  
       time.Sleep(time.Second * 1)  
       c1 <- "Time Over"  
}() 
select {  
    case msg1 := <-c1:
        fmt.Println("time out", msg1)  
    case msg2 := <-c2:
        fmt.Println("received", msg2)  
}  

这里的select会轮询两个条件,直到有一个满足,则执行对应的操作。如果DoSth在一秒内执行完成,c2中会存入数据,select执行输出received的操作,相反过了一秒DoSth没完成,c1中存入数据,select收到c1的数据,就执行输出time out的操作。

select {  
    case msg1 := <-c1:
        fmt.Println("time out", msg1)  
    case msg2 := <-c2:
        fmt.Println("received", msg2)  
    default:
        fmt.Println("default")
}  

如果select中定义了default标签,select立即返回结果,如果c1,c2都为空,则执行default操作。

总结

除了这里的这些特性,学习golang自然还少不了众多的第三方的包。不过这个要在以后的日子里一点一点积累。
这次总结知识梳理了一下golang的一些用法,要想真正掌握这些特性,还是需要把这些东西结合到实际的应用中。

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

推荐阅读更多精彩内容