Go基础

变量

变量声明:var name type

声明变量时若赋了初始值,可以省略类型。

可以同时声明多个相同类型的变量,如 var a,b = 100,3

定义多个不同类型的变量可如下定义

var (
    name1 = initialvalue1
    name2 = initialvalue2
)

对于局部变量,可以使用简洁的语法声明变量,如 name:=initialvalue

注意:局部变量声明之后若不使用会报错,且:=左边的变量至少要有一个是之前未声明的

当使用:=声明多个变量时,多个变量的类型可以不同。

类型

  • bool
  • Numeric Types
    • int8, int16, int32, int64, int
    • uint8, uint16, uint32, uint64, uint
    • float32, float64
    • complex64, complex128
    • byte
    • rune
  • string

数值类型进行计算时,不同数值类型不会自动转换,需要强转

函数

函数声明如下:

func functionname(parametername type) returntype {  
 //function body
}

当函数返回多个值时,可以使用 _ 接收不需要的返回值。

导入包

import (
    packagename
)

每个包都可以有一个init函数,用来执行初始化,当包被导入时该函数会被执行。

对于导入的包,若之后不使用包的内容,编译时会报错。但是某种情况下又希望执行该包的init函数,此时可以如下导包

import (  
    "geometry/rectangle" 
)

结构体

对于匿名结构体,他的类型就是他的”键“。

当结构体中包含一个匿名结构体,该匿名结构体的字段对于外面的结构体来说称为递进字段,可以直接访问。

package main

import (  
    "fmt"
)

type Address struct {  
    city  string
    state string
}
type Person struct {  
    name string
    age  int
    Address
}

func main() {  
    p := Person{
        name: "Naveen",
        age:  50,
        Address: Address{
            city:  "Chicago",
            state: "Illinois",
        },
    }

    fmt.Println("Name:", p.name)
    fmt.Println("Age:", p.age)
    fmt.Println("City:", p.city)   //city is promoted field
    fmt.Println("State:", p.state) //state is promoted field
}

类似的,对于属于匿名结构体的方法,外部的结构体也可以直接调用,就像是自己的方法。

接口

下面的例子说明了声明方法的不同方式带来的差异

package main

import "fmt"

type Describer interface {  
    Describe()
}
type Person struct {  
    name string
    age  int
}

func (p Person) Describe() { //implemented using value receiver  
    fmt.Printf("%s is %d years old\n", p.name, p.age)
}

type Address struct {  
    state   string
    country string
}

func (a *Address) Describe() { //implemented using pointer receiver  
    fmt.Printf("State %s Country %s", a.state, a.country)
}

func main() {  
    var d1 Describer
    p1 := Person{"Sam", 25}
    d1 = p1
    d1.Describe()
    p2 := Person{"James", 32}
    d1 = &p2
    d1.Describe()

    var d2 Describer
    a := Address{"Washington", "USA"}

    /* compilation error if the following line is
       uncommented
       cannot use a (type Address) as type Describer
       in assignment: Address does not implement
       Describer (Describe method has pointer
       receiver)
    */
    //d2 = a

    d2 = &a //This works since Describer interface
    //is implemented by Address pointer in line 22
    d2.Describe()

}

接口继承

package main

import (  
    "fmt"
)

type SalaryCalculator interface {  
    DisplaySalary()
}

type LeaveCalculator interface {  
    CalculateLeavesLeft() int
}

type EmployeeOperations interface {  
    SalaryCalculator
    LeaveCalculator
}

type Employee struct {  
    firstName string
    lastName string
    basicPay int
    pf int
    totalLeaves int
    leavesTaken int
}

func (e Employee) DisplaySalary() {  
    fmt.Printf("%s %s has salary $%d", e.firstName, e.lastName, (e.basicPay + e.pf))
}

func (e Employee) CalculateLeavesLeft() int {  
    return e.totalLeaves - e.leavesTaken
}

func main() {  
    e := Employee {
        firstName: "Naveen",
        lastName: "Ramanathan",
        basicPay: 5000,
        pf: 200,
        totalLeaves: 30,
        leavesTaken: 5,
    }
    var empOp EmployeeOperations = e
    empOp.DisplaySalary()
    fmt.Println("\nLeaves left =", empOp.CalculateLeavesLeft())
}

并发

Goroutines

Goroutines可以认为是轻量级的线程,用于并发的运行函数或方法。

Goroutines使用频道(channels)进行通信,channels就类似于管道(pipe)。

使用方法:在函数前使用go关键字

package main

import (  
    "fmt"
)

func hello() {  
    fmt.Println("Hello world goroutine")
}
func main() {  
    go hello()
    time.Sleep(1 * time.Second)
    fmt.Println("main function")
}

上面的例子中,会起一个goroutine来运行hello函数,原来的main函数则在main goroutine中运行。

Channels

声明chan T 是类型T的一个channel,用于传输类型为T的数据

可以使用make进行声明,如 a := make(chan int)

接收和发送数据:

data := <- a // read from channel a  
a <- data // write to channel a  

接收和发送数据默认是阻塞的,即当发送数据时控制过程会阻塞,直到有goroutine接收了数据。

demo:

package main

import (  
    "fmt"
)

func hello(done chan bool) {  
    fmt.Println("Hello world goroutine")
    done <- true
}
func main() {  
    done := make(chan bool)
    go hello(done)
    <-done
    fmt.Println("main function")
}

在循环中使用channel时需要使用close函数来关闭channel,避免死锁。

package main

import (  
    "fmt"
)

func producer(chnl chan int) {  
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {  
    ch := make(chan int)
    go producer(ch)
    for {
        v, ok := <-ch
        if ok == false {
            break
        }
        fmt.Println("Received ", v, ok)
    }
}
//和上面相同 不过使用的是range
func main() {  
    ch := make(chan int)
    go producer(ch)
    for v := range ch {
        fmt.Println("Received ",v)
    }
}

buffered channels

带有buffer的channel声明:ch := make(chan type, capacity)

带buffer的channel只有在buffer满了或为空(向channel发送数据/从channel接收数据)时才会阻塞。普通的channel可以认为是buffer capacity为0的buffered channel。

WaitGroup

WaitGroup类似于一道"屏障",只有当WaitGroup里面的goroutine全部执行完毕,才能继续往下走。

package main

import (  
    "fmt"
    "sync"
    "time"
)

func process(i int, wg *sync.WaitGroup) {  
    fmt.Println("started Goroutine ", i)
    time.Sleep(2 * time.Second)
    fmt.Printf("Goroutine %d ended\n", i)
    wg.Done()
}

func main() {  
    no := 3
    var wg sync.WaitGroup
    for i := 0; i < no; i++ {
        wg.Add(1)
        go process(i, &wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}

在上面的例子中,函数的waitgroup参数必须是个指针,否则每个函数拿到的只是waitgroup的一份copy,函数(此处也是goroutine)执行完后,mainroutine的wg也没有反应,从而造成死锁。

select

select会阻塞,直到select声明模块中至少有一个channel可用。对于同时有多个可用的channel,select会随机选择其他一个channel进行操作。

语法类似于switch,如下

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)
    select {
    case s1 := <-output1:
        fmt.Println(s1)
    case s2 := <-output2:
        fmt.Println(s2)
    }
}

mutex

mutex就是互斥量的意思,用于保证某一部分的代码同时只能有一个goroutine运行,其他goroutine会阻塞,直到上一个goroutine释放了锁。

package main  
import (  
    "fmt"
    "sync"
    )
var x  = 0  
func increment(wg *sync.WaitGroup, m *sync.Mutex) {  
    m.Lock()
    x = x + 1
    m.Unlock()
    wg.Done()   
}
func main() {  
    var w sync.WaitGroup
    var m sync.Mutex
    for i := 0; i < 1000; i++ {
        w.Add(1)        
        go increment(&w, &m)
    }
    w.Wait()
    fmt.Println("final value of x", x)
}

也可以使用channel完成上述例子的功能,因为channel本身就是阻塞的,代码如下

package main  
import (  
    "fmt"
    "sync"
    )
var x  = 0  
func increment(wg *sync.WaitGroup, ch chan bool) {  
    ch <- true
    x = x + 1
    <- ch
    wg.Done()   
}
func main() {  
    var w sync.WaitGroup
    ch := make(chan bool, 1)
    for i := 0; i < 1000; i++ {
        w.Add(1)        
        go increment(&w, ch)
    }
    w.Wait()
    fmt.Println("final value of x", x)
}

mutex和channel的选择

从上面可以看出mutex和channel可以完成同样的事情,那么mutex和channel该如何选择呢?

通常来讲,channel用于在goroutine间传递数据,而mutex用于限制只有一个goroutine进入的代码段(critical section)。

Defer

defer用于推迟函数或方法的执行,在函数或方法返回前才调用defer语句。下面的例子可以很好的理解的defer的使用。

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
    }
    fmt.Println("Largest number in", nums, "is", max)
}

func main() {  
    nums := []int{78, 109, 2, 563, 300}
    largest(nums)
}

上面的输出结果为:

Started finding largest
Largest number in [78 109 2 563 300] is 563
Finished finding largest

从上面可以看出defer的finished函数在largest函数里,直到largest返回时才执行finished函数,即finished函数是最后执行的。

defer函数的参数计算

defer函数的参数在defer语句执行时计算,而不是在实际函数调用完成时计算,具体的可以看下面的例子。

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

输出的结果为

value of a before deferred function call 10
value of a in deferred function 5

多个defer语句的执行

当一个函数里定义了多个defer语句,defer的执行顺序为LIFO,即后进先出。

package main

import (  
    "fmt"
)

func main() {  
    name := "Naveen"
    fmt.Printf("Original String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

结果为

Original String: Naveen
Reversed String: neevaN

遇到panic时,defer语句仍会执行

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

推荐阅读更多精彩内容

  • 基础 [TOC] 特性 Go 并发编程采用CSP模型不需要锁,不需要callback并发编程 vs 并行计算 安装...
    蕪園樓主香獨秀阅读 252评论 0 0
  • 函数的声明 在 Go 语言中,函数声明通用语法如下: 1.函数的声明以关键词 func开始 。 函数名和参数列表一...
    _羊羽_阅读 1,724评论 1 4
  • 运算符 算数运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符ps:为防止发生混淆,go语法规定,+...
    名字刚好七个字阅读 412评论 1 0
  • CSP 要想理解 channel 要先知道 CSP 模型。CSP 是 Communicating Sequenti...
    _羊羽_阅读 1,610评论 0 0
  • Go语言并发 Go 是并发式语言,而不是并行式语言。 并发是指立即处理多个任务的能力。 Go 编程语言原生支持并发...
    kakarotto阅读 1,894评论 0 7