併發支持,垃圾回收的編譯型系統編程語言。git
特色:express
Go環境變量編程
GOEXE=.exe // 造成可執行文件的後綴 GOPATH // 工做目錄
GOPATH下約定俗成的目錄:數組
bin // 存放編譯後生成的可執行文件 pkg // 存放編譯後生成的包文件 src // 存放項目源碼
經常使用命令緩存
go get // 獲取遠程包 (git或hg(若是是谷歌code上託管)) go run // 直接運行程序 go build // 編譯,檢查是否有編譯錯誤 go fmt // 格式化源碼 go install // 編譯包文件而且編譯整個程序 go test // 運行測試文件 go doc // 查看文檔 godoc -http=:8080 // 查看文檔
mpathapp // 可執行文件存放位置 math.a // 包文件
hello.go安全
package main import ( "fmt" ) func main () { fmt.Println("hello world") }
.go
文件的通常結構數據結構
Go程序是經過package
來組織的
只有package
名稱爲main
的包能夠包含main
函數
一個可執行程序有且僅有一個main
包多線程
經過import
關鍵字來導入其它非main
包
經過const
關鍵字來定義常量
經過在函數體外部使用var
關鍵字來進行全局變量的聲明和賦值
經過type
關鍵字來進行結構struct
或接口interface
的聲明
經過func
關鍵字來進行函數的聲明閉包
// 當前程序的包名 package main // 導入其它的包 // import . "fmt" // 省略調用 import ( "fmt" "io" "os" "time" "strings" ) // 常量的定義 const PI = 3.141592653 // 全局變量的聲明與賦值 var name = "zf" // 通常類型聲明 type newType int // 結構的聲明 type struc struct{} // 接口關鍵字和大括號不能有空格 // 接口的聲明 type inter interface{} // 接口關鍵字和大括號不能有空格 // 由 main 函數做爲程序入口點啓動 func main () { fmt.Println("hello world!") }
若是導入包以後,未調用其中的函數或者類型將會報出編譯錯誤.併發
package 別名
當使用第三方包時,包名可能會很是接近或者相同,此時就可使用別名來進行區別和調用
import std "fmt" // 別名 std.Println("hello world")
註釋
// 單行註釋 /* 多行註釋 */
可見性規則
Go語言中,使用大小寫來決定該常量,變量,類型,接口,結構,或函數是否能夠被外部包所調用:根據約定,函數名首字母小寫即爲private
;函數名首字母大寫即爲public
布爾型:bool
長度:1字節
取值範圍:true,false
注意:不能夠用數字表明true或false
整型:int/uint
根據運行平臺可能爲32或64位
8位整型:int8/uint8
長度:1字節 取值範圍:-128~127/0~255
16位整型:int16/uint16
長度:2字節 取值範圍:-32768~32767/0~65535
32位整型:int32(rune)/uint32
長度:4字節 取值範圍:-2^32/2~2^32/2-1/0~2^32-1
64位整型:int64/uint64
長度:8字節 取值範圍:-2^64/2~2^64/2-1/0~2^64-1
從嚴格意義上講type newint int
這裏的newint
ing不能說是int
的別名,而是底層數據結構相同,在這裏自定義類型,在進行類型轉換時扔舊須要顯示轉換,但byte
和rune
確實爲uint8
和int32
的別名,能夠相互進行轉換。
浮點型:float32/float64
長度:4/8字節
小數位:精確到7/15小數位
複數:complex64/complex128
長度:8/16字節
足夠保存指針的 32 位或 64 位整數型:uintptr
其它值類型
array、struct、string
引用類型
slice、map、chan
接口類型
interface
函數類型
func
零值並不等於空值,而是當變量被聲明爲某種類型後的默認值。
一般狀況下:
單個變量的聲明與賦值
變量的聲明格式:var <變量名稱> <變量類型>
變量的賦值格式: <變量名稱> = <表達式>
聲明的同時賦值:var <變量名稱> [變量類型] = <表達式>
寫法:
var a int // 變量聲明 a = 10 // 變量賦值 var b int = 20 // 變量聲明的同時賦值 var b = 1 // 變量聲明與賦值,由系統推薦是那種類型 b := 10 // 函數中的變量聲明與賦值的最簡寫法 var 是全局的變量 := 只能在函數中使用,局部變量
多個變量的聲明與賦值
全局變量的聲明可以使用var()
的方式進行簡寫
全局的變量的聲明不能夠省略var,但可以使用並行方式
全部變量均可以使用類型推斷
局部變量不可使用var()
的方式簡寫,只能使用並行方式
var ( // 使用常規方式 aaa = "hello" // 使用並行方式以及類型推斷 a, b = 1, 2 // cc := 2 // 不能夠省略 var ) func main () { // 多個變量的聲明 var a, b, c, d int // 多個變量的賦值 a, b, c, d = 1, 2, 3, 4 // 多個變量聲明的同時賦值 var e, f, g, h int = 5, 6, 7, 8 // 省略變量類型,由系統推斷 var i, j, k, l = 9, 10, 11, 12 // 多個變量聲明與賦值的最簡寫法 i, m, n, o := 13, 14, 15, 16 _, dd = 10, 20 // 空白符號,省略該表達式賦值(應用函數返回值) }
類型轉換的格式:
<ValueA> [:]= <TypeOfValueA>(<ValueB>)
// 在相互兼容的兩種類型之間轉換 var a float32 = 1.1 b := int(a) // 表達式沒法經過編譯 var c bool = true d := int(c)
package main import ( "fmt" ) func main () { var a float32 = 100.01 fmt.Println(a) // 100.01 b := int(a) fmt.Println(b) // 100 }
整型沒法和布爾型兼容float
類型沒法和字符串類型兼容
int
和string
互轉
var c int = 3 // d := string(c) d := strconv.Itoa(c) // 字符串 3 c, _ = strconv.Atoi(d) // int 3
現象:
var a int = 65 string(a) fmt.Println(a) // A
string()
表示將數據轉換成文本格式,由於計算機中存儲的任何東西本質上都是數字,所以此函數天然的認爲須要的是用數字65表示文本A
常量的初始化規則與枚舉
iota
是常量的計數器,從0開始,組中每定義1個常量自動遞增1 iota
能夠達到枚舉的效果iota
就會重置爲0const ( _A = "A" _B _C = iota _D ) func main () { fmt.Println(_A, _B, _C, _D) // A A 2 3 }
const ( a = "123" b = len(a) c ) func main () { fmt.Println(a, b, c) // 123, 3, 3 }
編譯不經過:
var ss = "123" const ( a = len(ss) b c ) func main () { fmt.Println(a, b, c) }
錯誤信息:
# command-line-arguments .\const.go:39: const initializer len(ss) is not a constant
const ( a, b = 1, "xixi" c ) func main () { fmt.Println(a, b, c) }
錯誤信息:
# command-line-arguments .\const.go:40: extra expression in const declaration
Go中的運算符從左至右結合
優先級(從高到低)
^
!
(一元運算符)*
/
%
<<
>>
&
&^
(二元運算符)+
-
|
^
(二元運算符)==
!=
<
<=
>=
>
(二元運算符)<-
(專門用於channel)&&
||
fmt.Println(1 ^ 2) // 二元運算符 fmt.Println(^2) // 一元運算符 /* 6: 0110 11: 1101 ------------ & 0010 // 2 | 1111 // 15 ^ 1101 // 13 &^ 0100 // 4 6 -> 110 5 -> 101 4 -> 100 13 / 2 = 1 // 6 1101 */
指針
Go雖然保留了指針,但與其餘編程語言不一樣的是,在Go當中不支持指針運算以及->
運算符,而是直接採用.
選擇符來操做指針目標對象的成員
&
取變量地址,使用*
經過指針間接訪問目標對象nil
而非NULL
package main import ( "fmt" ) func main () { a := 1 var p *int = &a fmt.Println(p) // 0xc0420361d0 fmt.Println(*p) // 1 }
遞增遞減語句
在Go當中,++
與--
是做爲語句而並非做爲表達式
判斷if
block
級別,同時隱藏外部同名變量func main () { a := 10 if a := 0; a > 0 { fmt.Println(a) } else if a == 0 { fmt.Println(0111) // 73 } fmt.Println(a) // 10 }
循環for
// 第一種形式 func main () { a := 1 for { a++ if a > 3 { break } fmt.Println(a) // 2, 3 } fmt.Println(a) // 4 }
// 第二種形式 func main () { a := 1 for a <= 3 { a++ fmt.Println(a) // 2, 3, 4 } fmt.Println(a) // 4 }
// 第三種形式 func main () { a := 1 for i := 0; i < 3; i++ { a++ fmt.Println(a) // 2, 3, 4 } fmt.Println(a) // 4 }
swtich
func main () { a := 1 switch a { case 0: fmt.Println("a=0") case 1: fmt.Println("a=1") } fmt.Println(a) }
func main () { a := 1 switch { case a >= 0: fmt.Println("a>=0") fallthrough case a >= 1: fmt.Println("a>=1") } fmt.Println(a) }
func main () { switch a := 1; { case a >= 0: fmt.Println("a>=0") fallthrough case a >= 1: fmt.Println("a>=1") default: fmt.Println("none") } fmt.Println(a) // undefined: a //for,if,switch都具備塊級做用域 }
跳轉語句goto,break,continue
break
與continue
配合標籤可用於多層循環的跳出goto
調整執行位置,與其它2個語句配合標籤的結果並不相同func main () { LABEL: for { for i := 0; i < 10; i++ { if i > 2 { break LABEL } else { fmt.Println(i) } } } }
func main () { LABEL: for i := 0; i < 10; i++ { for { fmt.Println(i) continue LABEL } } }
==
或!=
進行比較,但不可使用<
或>``(相同類型之間,纔可使用相等或不能判斷。也就是數組長度也要相同,長度也是數組類型的一部分)建立數組
func main () { var a [2]string var b [1]int c := [2]int{11, 12} d := [20]int{19: 1} e := [...]int{1, 2, 3, 4, 5} f := [...]int{0: 11, 1: 22, 2: 33} b[0] = 10 a[1] = "100" arr := [...]string{0: "xixi", 1: "hhh"} fmt.Println(a, b, c, d, e, f) }
p := new([10]int) p[1] = 2 fmt.Println(&p) // 取地址 fmt.Println(*p) // 取值
// 多維數組 a := [2][3]int{ {1, 1, 1}, {2, 2, 2}, } fmt.Println(a)
冒泡排序:
func main () { a := [...]int{3, 4, 234, 2, 3, 5} fmt.Println(a) num := len(a) for i := 0; i< num; i++ { for j := i+1; j < num; j++ { if a[i] < a[j] { temp := a[i] a[i] = a[j] a[j] = temp } } } fmt.Println(a) }
len()
獲取元素個數,cap()
獲取容量make()
建立slice
指向相同底層數組,其中一個的值改變會影響所有make([]T, len, cap)
其中`cap`能夠省略,則和`len`的值相同 `len`表示存數的元素個數,`cap`表示容量
聲明:
// 聲明方法: var s1 []int // 中括號中沒有數字或`...` fmt.Println(s1) // []
reslice方法: 從數組中截取Slice
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println(a) s1 := a[5: len(a)] // 包含起始索引,不包含終止索引 // a[5 6 7 8 9] s2 := a[5: ] // 包含起始索引,不包含終止索引 // a[5 6 7 8 9] fmt.Println(s1, s2)
make方法 (通常使用make建立)
s1 := make([]int, 3, 10) // 10小塊連續的內存,若是slice超過10,內存卡會繼續申請,從新生成內存地址 s2 := make([]int, 10) // cap不給定,是slice的最大長度 fmt.Println(len(s1), cap(s1), s1) // 3 10 [0 0 0] fmt.Println(len(s2), cap(s2), s2) // 10 10 [0 0 0 0 0 0 0 0 0 0]
Reslice
reslice
時索引以被slice
的切片爲準slice
的切片的容量cap()
值Append
slice
尾部追加元素slice
追加在另外一個slice
尾部slice
的容量則返回元素slice
slice
的容量則將從新分配數組並拷貝原始數據s1 := make([]int, 3, 6) fmt.Println("%p\n", s1) s1 = append(s1, 1, 2, 3, 4) fmt.Println("%v %p\n", s1) // [0 0 0 1 2 3 4]
Copy
nt{1, 2, 3, 4, 5, 6, 7} s2 := []int{8, 9} copy(s1, s2) // copy(s1, s2[1: 2]) fmt.Println(s1, s2) // [8 9 3 4 5 6 7] [8 9]
==
或!=
比較運算符的類型,不能夠是函數,map或slice make()
建立,支持:=
簡寫方式delete()
刪除某鍵值對for range
對map
和slice
進行迭代操做make([keyType]valueType, cap), cap表示容量,可省略
超過容量時會自動擴容,但儘可能提供一個合理的初始值 使用`len()`獲取元素個數
Map初始化
var m map[int]string // m = map[int]string{} m = make(map[int]string) var m1 map[int]string = make(map[int]string) m2 := make(map[int]string) fmt.Println(m, m1) // map[]
刪除和經常使用Map方法賦值
m2 := make(map[int]string) m2[1] = "OK" delete(m2, 1) a := m2[1] fmt.Println(a)
迭代:
for i,v := range slice { // slice[i] } for k,v := range map { // map[k] }
取Map中的key
m := map[int]string{1: "A", 2: "B", 3: "C", 4: "D"} s := make([]int, len(m)) i := 0 for k,_ := range m { s[i] = k i++ } sort.Ints(s) fmt.Println(s)
Map中的 key-value互換:
m1 := map[int]string{1: "A", 2: "B", 3: "C"} m2 := make(map[string]int) // m2 := map[string]int{"A": 1, "B": 2, "C": 3} for k,v := range m1 { m2[v] = k } fmt.Println(m1) fmt.Println(m2)
函數function
Go 函數 不支持嵌套、重載和默認參數
支持的特性:
定義函數使用關鍵字 func,且左大括號不能另起一行
函數也能夠做爲一種類型使用
閉包:
func closure (x int) func (int) int { return func (y int) int { return x + y } }
defer
defer
的執行方式相似其它語言中的析構函數,在函數體執行結束後按照調用順序的相反順序逐個執行defer
時匿名函數的參數,則在定義defer時即已經得到了拷貝,不然則是引用某個變量的地址panic/recover
模式來處理錯誤panic
能夠在任何地方引起,但recover
只有在defer調用的函數中有效defer使用:
func main () { fmt.Println("a") defer fmt.Println("b") defer fmt.Println("c") // a, c, b for i := 0; i < 3; i++ { defer fmt.Println(i) // 2 1 0 } for i := 0; i < 3; i++ { defer func () { fmt.Println(i) // 3 3 3 }() } }
defer
要放在panic()
以前:
func main () { A() B() C() } func A () { fmt.Println("func A") } func B () { defer func () { if err := recover(); err != nil { fmt.Println("Recover") } }() panic("Panic in B") } func C () { fmt.Println("func C") }
struct
與C中的struct
很是類似,而且Go沒有class
type <Name> struct{}
定義結構,名稱遵循可見性規則map
的值==
與 !=
比較運算符,但不支持 >
或 <
type person struct { name string age int } func main () { a := person{} a.name = "zf" a.age = 23 fmt.Println(a) // { 0} }
struct
也是值類型
對初始化結構struct
使用地址符
type person struct { name string age int } func main () { a := &person{ // 調用結構使用地址符 // 字面值初始化 name: "zf", age: 24, } a.name = "pink" // a.name = "zf" // a.age = 23 fmt.Println(a) // { 0} // A(&a) A(a) B(a) fmt.Println(a) } func A (per *person) { per.age = 18 fmt.Println("A", per) } func B (per *person) { per.age = 20 fmt.Println("B", per) }
匿名結構:
func main () { a := &struct { name string age int } { name: "tan", age: 19, } fmt.Println(a) }
外層結構:
type person struct { name string age int contact struct { phone,city string } } func main () { b := person { name: "yellow", age: 18, } b.contact.phone = "123123" b.contact.city = "xiamen" fmt.Println(b) }
匿名字段:
type p1 struct { string int } func main () { c := p1{"cyan", 20} // 字段的類型嚴格按照結構聲明的字段 fmt.Println(a, b, c) }
匿名函數和匿名字段在函數中使用的次數很是少,沒有必要聲明,纔會使用到。
嵌入(繼承)結構:
type human struct { Sex int } type teacher struct { human name string age int } type student struct { human name string age int } func main () { // a := teacher{name: "cyan", age: 20, human{sex: 0}} a := teacher{name: "cyan", age: 20, human: human{Sex: 0}} b := student{name: "pink", age: 22, human: human{Sex: 1}} a.name = "xixi" a.age = 23 // a.Sex = 100 a.human.Sex = 200 fmt.Println(a, b) }
class
,但依舊有method
receiver
來實現與某個類型的組合Receiver
能夠是類型的值或者指針type Test struct { name string } type Person struct { name string } func main () { t := Test{} t.Print() fmt.Println(t.name) p := Person{} p.Print() fmt.Println(p.name) } func (t *Test) Print() { t.name = "red" fmt.Println("Test") } func (p Person) Print() { fmt.Println("Person") }
// 類型別名不會擁有底層類型所附帶的方法 type TZ int func main () { var a TZ a.Print() (*TZ).Print(&a) } func (a *TZ) Print() { fmt.Println("TZ") }
方法不一樣調用方式
type A struct { name string } func main () { a := A{} a.Print() // (*TZ).Print(&a) } func (a *A) Print() { a.name = "123" fmt.Println(a.name) // fmt.Println("TZ") }
方法訪問權限struct
中的私有屬性,在方法中能夠訪問
// 屬性的訪問範圍是在`package`中的能夠訪問的,若是須要在外部包中訪問,須要大寫字母 type A struct { name string } func main () { a := A{} a.Print() fmt.Println(a.name) } func (a *A) Print() { a.name = "123" fmt.Println(a.name) }
Structural Typing
receiver
type USB interface { Name() string Connect() } type Phone struct { name string } func (pc Phone) Name() string { return pc.name } func (pc Phone) Connect() { fmt.Println("Connect: ", pc.name) } func main () { var a USB a = Phone{name: "phone"} a.Connect() Disconnect(a) } func Disconnect(usb USB) { fmt.Println("Disconnect") }
接口嵌套:
type USB interface { Name() string // Connect() Connecter } type Connecter interface { Connect() } type Phone struct { name string } func (pc Phone) Name() string { return pc.name } func (pc Phone) Connect() { fmt.Println("Connect: ", pc.name) } func main () { var a USB a = Phone{name: "phone"} a.Connect() Disconnect(a) } func Disconnect(usb USB) { fmt.Println("Disconnect") }
空接口的使用:
// Go語言中全部類型都實現空接口 // type empty interface { // } type USB interface { Name() string // Connect() Connecter } type Connecter interface { Connect() } type Phone struct { name string } func (pc Phone) Name() string { return pc.name } func (pc Phone) Connect() { fmt.Println("Connect: ", pc.name) } func main () { var a USB a = Phone{name: "phone"} a.Connect() Disconnect(a) // 空接口的判斷 var b interface{} fmt.Println(b == nil) // true } func Disconnect(usb interface{}) { // interface{} 空接口 // if pc,ok := usb.(Phone); ok { // 類型判斷 // fmt.Println("Disconnect:", pc.name) // return // } switch v := usb.(type) { case Phone: fmt.Println("Disconnect:", v.name) default : fmt.Println("Unknown") } fmt.Println("Disconnect") }
只有當接口存儲的類型和對象都爲nil時,接口才等於nil
func main () { // 空接口的判斷 var b interface{} fmt.Println(b == nil) // true var p *int = nil b = p fmt.Println(b == nil) // false }
類型斷言
經過類型斷言的ok pattern
能夠判斷接口中的數據類型
使用type switch
則可針對空接口進行比較全面的類型判斷
type USB interface { Name() string // Connect() Connecter } type Connecter interface { Connect() } func Disconnect(usb interface{}) { // interface{} 空接口 // if pc,ok := usb.(Phone); ok { // 類型判斷 // fmt.Println("Disconnect:", pc.name) // return // } switch v := usb.(type) { case Phone: fmt.Println("Disconnect:", v.name) default : fmt.Println("Unknown") } fmt.Println("Disconnect") }
接口轉換
能夠將擁有超集的接口轉換爲子集的接口
interface{}
有更大的發揮餘地TypeOf
和ValueOf
函數從接口中獲取目標對象信息interface.data
是settabel
即pointer-interface
package main import ( "fmt" "reflect" ) type User struct { Id int Name string Age int } func (u User) Hello() { fmt.Println("Hello world") } func main () { u := User{1, "alogy", 12} Info(u) } func Info(o interface{}) { t := reflect.TypeOf(o) fmt.Println("Type: ", t.Name()) v := reflect.ValueOf(o) fmt.Println("Fields: ") for i := 0; i < t.NumField(); i++ { f := t.Field(i) val := v.Field(i).Interface() fmt.Println(f.Name, f.Type, val) } for i := 0; i < t.NumMethod(); i++ { m := t.Method(i) fmt.Println(m.Name, m.Type) } }
若是是地址引用經過Kind()
來獲取與reflect.Struct
匹配的對象。
package main import ( "fmt" "reflect" ) type User struct { Id int Name string Age int } func (u User) Hello() { fmt.Println("Hello world") } func main () { u := User{1, "alogy", 12} Info(&u) } func Info(o interface{}) { t := reflect.TypeOf(o) fmt.Println("Type: ", t.Name()) fmt.Println(t.Kind()) if k := t.Kind(); k != reflect.Struct { fmt.Println("XX") return } v := reflect.ValueOf(o) fmt.Println("Fields: ") for i := 0; i < t.NumField(); i++ { f := t.Field(i) val := v.Field(i).Interface() fmt.Println(f.Name, f.Type, val) } for i := 0; i < t.NumMethod(); i++ { m := t.Method(i) fmt.Println(m.Name, m.Type) } }
匿名字段反射:
package main import ( "fmt" "reflect" ) type User struct { Id int Name string Age int } type Manager struct { User title string } func main () { // 反射會將匿名字段看成獨立字段來處理 m := Manager{User: User{1, "OK", 12}, title: "123123"} t := reflect.TypeOf(m) fmt.Println(t.Field(0)) // 取匿名當中的字段 fmt.Println(t.FieldByIndex([]int{0, 0})) }
指針操做:
package main import ( "fmt" "reflect" ) func main () { x := 123 v := reflect.ValueOf(&x) // fmt.Println(v) v.Elem().SetInt(999) fmt.Println(x) }
類型判斷修改字段:
package main import ( "fmt" "reflect" ) type User struct { Id int Name string Age int } func main () { u := User{1, "Ok", 18} Set(&u) fmt.Println(u) } func Set(o interface{}) { v := reflect.ValueOf(o) if reflect.Ptr == v.Kind() && !v.Elem().CanSet() { // 指針是否正確 fmt.Println("XXX") return } else { v = v.Elem() // 重寫賦值 } f := v.FieldByName("Name") // 獲取字段 if !f.IsValid() { // 判讀是否取到當前字段 fmt.Println("BAD") return } if f.Kind() == reflect.String { // 類型判斷 f.SetString("MM") // 從新賦值 } }
經過反射調用方法,動態調用方法:
package main import ( "fmt" "reflect" ) type User struct { Id int Name string Age int } func (u User) Hello(name string) { fmt.Println("Hello", name, ", my name is", u.Name) } func main () { u := User{1, "OK", 123} v := reflect.ValueOf(u) mv := v.MethodByName("Hello") // 獲取函數名 args := []reflect.Value{reflect.ValueOf("alogy")} // 傳遞參數 mv.Call(args) u.Hello("alogy") }
併發主要由切換時間片來實現「同時」運行,在並行則是直接利用多核實現多線程的運行,但Go能夠設置使用核數,以發揮多核計算機的能力。
Goroutine奉行經過通訊來共享內存,而不是共享內存來通訊。
package main import ( "fmt" "time" ) func main () { fmt.Println(2 * time.Second) // 2s go Go() time.Sleep(2 * time.Second) // 延遲2s // 在main函數運行Sleep的時候,Go函數也運行了,執行完以後,退出。 } func Go () { fmt.Println("Go...") }
Channel
Channel
是goroutine
溝通的橋樑,大都是阻塞同步的.經過關鍵字go
加函數的名稱,來實現goroutine
make
建立,close
關閉Channel
是引用類型for range
來迭代不斷操做的Channel
Select
channel
的發送與接收channel
時按隨機順序處理select
來阻塞main
函數channel簡單使用:
package main import ( "fmt" ) func main () { c := make(chan bool) go func () { fmt.Println("Go...") c <- true // 存 // 聲明的時候是bool }() <-c // 取 // 消息存取,阻塞執行 }
package main import ( "fmt" ) func main() { c := make(chan bool) go func() { fmt.Println("Go...") c <- true close(c) // 關閉chan // 沒有明確關閉,會出現死鎖,崩潰退出 }() // <- c for v := range c { fmt.Println(v) } }
有無緩存區別
有緩存:異步,無緩存,同步。
package main import ( "fmt" ) func main() { // c := make(chan bool, 1) // 有緩存 異步 c := make(chan bool) // 無緩存的時候是阻塞的 go func () { fmt.Println("Go...") <- c // 讀取 }() c <- true // 傳進去 }
多核分配任務
出現任務分配出現不必定的狀況,解決方法(多個goroutine
的打印):
sync
package main import ( "fmt" "runtime" ) func main() { runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核分配的時候,任務分配時不必定的 c := make(chan bool, 10) // 設置緩存 for i := 0; i < 10; i++ { go Go(c, i) } for i := 0; i < 10; i++ { <-c } } func Go(c chan bool, idx int) { a := 1 for i := 0; i < 10000000; i++ { a += i } fmt.Println(idx, a) c <- true }
package main import ( "fmt" "runtime" "sync" ) func main() { runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核分配的時候,任務分配時不必定的 wg := sync.WaitGroup{} // 內置包sync使用 wg.Add(10) for i := 0; i < 10; i++ { go Go(&wg, i) } wg.Wait() } func Go(wg *sync.WaitGroup, idx int) { a := 1 for i := 0; i < 10000000; i++ { a += i } fmt.Println(idx, a) wg.Done() }
多個chan
經過select語句
package main import ( "fmt" ) func main() { c1, c2 := make(chan int), make(chan string) o := make(chan bool, 2) go func() { for { // 經過死循環來不斷髮送和接收chan select { case v, ok := <-c1 : if !ok { o <- true break } fmt.Println("c1", v) case v, ok := <- c2 : if !ok { o <- true break } fmt.Println("c2", v) } } }() c1 <- 1 c2 <- "hello" c1 <- 2 c2 <- "zf" close(c1) close(c2) for i := 0; i < 2; i++ { <-o } }
select做爲發送者的應用
package main import ( "fmt" ) func main() { c := make(chan int) go func() { for v := range c { fmt.Println(v) } }() for i := 0; i < 10; i++ { select { case c <- 0: case c <- 1: } } // select{} // 阻塞main函數退出,卡死main函數,場景常用在事件循環 }
select超時設置
package main import ( "fmt" "time" ) func main() { c := make(chan bool) select { case v := <-c: fmt.Println(v) case t := <-time.After(3 * time.Second): fmt.Println(t) fmt.Println("Timeout") } }