并发支持,垃圾回收的编译型系统编程语言。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") } }