变量
变量声明: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语句仍会执行