golang通道实现生产者、消费者
package main
import (
"fmt"
"math/rand"
"time"
)
// 数据生产者
func producer(header string, channel chan<-string) {
for {
channel <- fmt.Sprintf("%s: %v", header, rand.Int31())
time.Sleep(1*time.Second)
}
}
// 数据消费者
func customer(channel <-chan string) {
for {
message := <-channel
fmt.Println(message)
}
}
func main() {
channel := make(chan string)
go producer("cat", channel)
go producer("dog", channel)
customer(channel)
}
go语言为任意类型添加方法
package main
import "fmt"
type MyInt int
func (m MyInt) IsZero() bool {
return m == 0
}
func (m MyInt) Add(other int) int {
return other + int(m)
}
func main() {
var b MyInt
fmt.Println(b.IsZero())
b = 1
fmt.Println(b.Add(2))
}
结构体内嵌–"继承"
// 结构体内嵌特性:
// 1)内嵌的结构体,外部结构体可以直接访问其成员变量。
// 2)内嵌结构体的字段名是它的类型名。
package main
import "fmt"
// 结构体内嵌
type A struct {
ax, ay int
}
type B struct {
A
bx, by float32
}
func main() {
b := B{A{1, 2}, 3.0, 4.0}
fmt.Println(b.ax, b.ay, b.bx, b.by)
fmt.Println(b.A)
}
结构体内嵌模拟类的继承
package main
import "fmt"
type Flying struct{}
func (f *Flying) Fly() {
fmt.Println("can fly")
}
type Walkable struct {}
func (f *Walkable) Walk() {
fmt.Println("can walk")
}
type Human struct {
Walkable
}
type Bird struct {
Walkable
Flying
}
func main() {
b := new(Bird)
fmt.Println("Bird: ")
b.Fly()
b.Walk()
h := new(Human)
fmt.Println("Human: ")
h.Walk()
}
//无缓冲通道模拟两个协程间的网球比赛
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
// 利用无缓冲通道模拟两个协程间的比赛
var wg sync.WaitGroup
func init() {
rand.Seed(time.Now().UnixNano())
}
// player 模拟一个选手在打网球
func player(name string, court chan int) {
// 在函数退出时调用Done来通知main函数工作已经完成
defer wg.Done()
for {
ball, ok := <- court
if !ok {
fmt.Printf("player %s win\n", name)
return
}
// 随机数,判断选手是否击球
n := rand.Intn(100)
if n%13 == 0 {
fmt.Printf("player %s missed\n", name)
close(court)
return
}
// 显示击球数,并将击球数加1
fmt.Printf("player %s hit %d \n", name, ball)
ball++
// 将球打向对手
court <- ball
}
}
func main() {
// 无缓冲通道
court := make(chan int)
wg.Add(2)
// 启动两个选手
go player("Nadal", court)
go player("Djokovic", court)
// 发球
court <- 1
// 等待游戏结束
wg.Wait()
}
// channel超时机制
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
quit := make(chan bool)
// 新开一个协程
go func() {
for {
select {
case num := <-ch:
fmt.Println("num = ", num)
case <- time.After(3 * time.Second):
fmt.Println("超时")
quit <- true
}
}
}()
for i := 0; i < 5; i++ {
ch <- i
time.Sleep(time.Second)
}
<- quit
fmt.Println("程序结束")
}
失控的goroutine
package main
import (
"fmt"
"runtime"
)
func consumer(ch chan int) {
// 无限获取数据的循环
for {
data := <-ch
fmt.Println(data)
}
}
func main() {
// 创建一个传递数据用的通道
ch := make(chan int)
for {
var dummy string
// 获取输入,模拟进程持续进行
fmt.Scan(&dummy)
go consumer(ch)
// 输出当前的goroutine数量
fmt.Println("num of goroutines:", runtime.NumGoroutine())
}
}
go变量生命周期和逃逸分析
堆(heap):堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态加入到堆上(堆被扩张)。当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减);
栈(stack):栈又称堆栈, 用来存放程序暂时创建的局部变量,也就是我们函数的大括号{ }中定义的局部变量。
package main
var global *int
func f() {
var x int
x = 1
global = &x
}
func g() {
y := new(int)
*y = 1
}
func main() {
f()
g()
}
golang变量逃逸分析
go run -gcflags "-m -l" main.go
使用 go run 运行程序时,-gcflags 参数是编译参数。其中 -m 表示进行内存分配分析,-l 表示避免程序内联,也就是避免进行程序优化。
上述代码中,函数 f 里的变量 x 必须在堆上分配,因为它在函数退出后依然可以通过包一级的 global 变量找到,虽然它是在函数内部定义的。用Go语言的术语说,这个局部变量 x 从函数 f 中逃逸了。
相反,当函数 g 返回时,变量 *y 不再被使用,也就是说可以马上被回收的。因此,*y 并没有从函数 g 中逃逸,编译器可以选择在栈上分配 *y 的存储空间,也可以选择在堆上分配,然后由Go语言的 GC(垃圾回收机制)回收这个变量的内存空间。
在实际的开发中,并不需要刻意的实现变量的逃逸行为,因为逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。
golang中的nil(空值/零值)
nil 是Go语言中一个预定义好的标识符,nil 是 map、slice、pointer、channel、func、interface 的零值。
package main
import "fmt"
func main() {
var m map[int]string
var ptr *int
var c chan int
var sl []int
var f func()
var i interface{}
fmt.Printf("%#v\n", m)
fmt.Printf("%#v\n", ptr)
fmt.Printf("%#v\n", c)
fmt.Printf("%#v\n", sl)
fmt.Printf("%#v\n", f)
fmt.Printf("%#v\n", i)
}
//map[int]string(nil)
//(*int)(nil)
//(chan int)(nil)
//[]int(nil)
//(func())(nil)
//<nil>
golang 指针类型的接收器
// 指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的this或者self。
// 由于指针的特性,调用方法时,修改接收器指针的任意成员变量,在方法结束后,修改都是有效的。
package main
import "fmt"
// 定义属性结构
type Property struct {
value int
}
// 设置属性值
func (p *Property) SetValue(v int) {
p.value = v
}
// 取属性值
func (p *Property) Value() int {
return p.value
}
func main() {
// 实例化属性
p := new(Property)
p.SetValue(100)
fmt.Println(p.value)
}