變量
變量聲明:var name type
聲明變量時若賦了初始值外驱,可以省略類型。
可以同時聲明多個相同類型的變量俊性,如 var a,b = 100,3
定義多個不同類型的變量可如下定義
var (
name1 = initialvalue1
name2 = initialvalue2
)
對于局部變量略步,可以使用簡潔的語法聲明變量,如 name:=initialvalue
注意:局部變量聲明之后若不使用會報錯定页,且:=左邊的變量至少要有一個是之前未聲明的
當(dāng)使用:=聲明多個變量時趟薄,多個變量的類型可以不同。
類型
- bool
- Numeric Types
- int8, int16, int32, int64, int
- uint8, uint16, uint32, uint64, uint
- float32, float64
- complex64, complex128
- byte
- rune
- string
數(shù)值類型進行計算時典徊,不同數(shù)值類型不會自動轉(zhuǎn)換杭煎,需要強轉(zhuǎn)
函數(shù)
函數(shù)聲明如下:
func functionname(parametername type) returntype {
//function body
}
當(dāng)函數(shù)返回多個值時,可以使用 _ 接收不需要的返回值卒落。
包
導(dǎo)入包
import (
packagename
)
每個包都可以有一個init函數(shù)羡铲,用來執(zhí)行初始化,當(dāng)包被導(dǎo)入時該函數(shù)會被執(zhí)行儡毕。
對于導(dǎo)入的包也切,若之后不使用包的內(nèi)容扑媚,編譯時會報錯。但是某種情況下又希望執(zhí)行該包的init函數(shù)雷恃,此時可以如下導(dǎo)包
import (
"geometry/rectangle"
)
結(jié)構(gòu)體
對于匿名結(jié)構(gòu)體疆股,他的類型就是他的”鍵“。
當(dāng)結(jié)構(gòu)體中包含一個匿名結(jié)構(gòu)體倒槐,該匿名結(jié)構(gòu)體的字段對于外面的結(jié)構(gòu)體來說稱為遞進字段旬痹,可以直接訪問。
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
}
類似的讨越,對于屬于匿名結(jié)構(gòu)體的方法两残,外部的結(jié)構(gòu)體也可以直接調(diào)用,就像是自己的方法把跨。
接口
下面的例子說明了聲明方法的不同方式帶來的差異
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())
}
并發(fā)
Goroutines
Goroutines可以認為是輕量級的線程人弓,用于并發(fā)的運行函數(shù)或方法。
Goroutines使用頻道(channels)進行通信节猿,channels就類似于管道(pipe)票从。
使用方法:在函數(shù)前使用go關(guān)鍵字
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函數(shù)滨嘱,原來的main函數(shù)則在main goroutine中運行峰鄙。
Channels
聲明:chan T 是類型T的一個channel,用于傳輸類型為T的數(shù)據(jù)
可以使用make進行聲明太雨,如 a := make(chan int)
接收和發(fā)送數(shù)據(jù):
data := <- a // read from channel a
a <- data // write to channel a
接收和發(fā)送數(shù)據(jù)默認是阻塞的吟榴,即當(dāng)發(fā)送數(shù)據(jù)時控制過程會阻塞,直到有g(shù)oroutine接收了數(shù)據(jù)囊扳。
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")
}
在循環(huán)中使用channel時需要使用close函數(shù)來關(guān)閉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發(fā)送數(shù)據(jù)/從channel接收數(shù)據(jù))時才會阻塞锥咸。普通的channel可以認為是buffer capacity為0的buffered channel狭瞎。
WaitGroup
WaitGroup類似于一道"屏障",只有當(dāng)WaitGroup里面的goroutine全部執(zhí)行完畢搏予,才能繼續(xù)往下走熊锭。
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")
}
在上面的例子中,函數(shù)的waitgroup參數(shù)必須是個指針雪侥,否則每個函數(shù)拿到的只是waitgroup的一份copy碗殷,函數(shù)(此處也是goroutine)執(zhí)行完后,mainroutine的wg也沒有反應(yīng)速缨,從而造成死鎖锌妻。
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間傳遞數(shù)據(jù)捡偏,而mutex用于限制只有一個goroutine進入的代碼段(critical section)。
Defer
defer用于推遲函數(shù)或方法的執(zhí)行峡迷,在函數(shù)或方法返回前才調(diào)用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)
}
上面的輸出結(jié)果為:
Started finding largest
Largest number in [78 109 2 563 300] is 563
Finished finding largest
從上面可以看出defer的finished函數(shù)在largest函數(shù)里绘搞,直到largest返回時才執(zhí)行finished函數(shù)彤避,即finished函數(shù)是最后執(zhí)行的。
defer函數(shù)的參數(shù)計算
defer函數(shù)的參數(shù)在defer語句執(zhí)行時計算夯辖,而不是在實際函數(shù)調(diào)用完成時計算琉预,具體的可以看下面的例子。
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)
}
輸出的結(jié)果為
value of a before deferred function call 10
value of a in deferred function 5
多個defer語句的執(zhí)行
當(dāng)一個函數(shù)里定義了多個defer語句蒿褂,defer的執(zhí)行順序為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)
}
}
結(jié)果為
Original String: Naveen
Reversed String: neevaN
遇到panic時啄栓,defer語句仍會執(zhí)行