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语句仍会执行

你可能感兴趣的:(Go基础)