1.struct结构体能不能比较
结构体不可以比较,但是同一类型的结构体的值可以比较是否相等的(不可以比较大小):
- 结构体所有字段的值都相等,两个结构体才相等
- 比较的两个结构体必须是相同类型才可以,也就是说他们字段的顺序、名称、类型、标签都相同才可以
2. defer的顺序
先注册,后执行;后注册,先执行。
3.select用法
select就是用来监听和channel有关的IO操作,当 IO 操作发生时,触发相应的动作。类似linux中的select,io多路复用机制。可以用作协程的退出。
//select基本用法
select {
case <- chan1:
// 如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
// 如果成功向chan2写入数据,则进行该case处理语句
default:
// 如果上面都没有成功,则进入default处理流程
注意事项:
- 如果有一个或多个IO操作可以完成,则Go运行时系统会随机的选择一个执行,否则的话,如果有default分支,则执行default分支语句,如果连default都没有,则select语句会一直阻塞,直到至少有一个IO操作可以进行。
- 所有channel表达式都会被求值、所有被发送的表达式都会被求值。求值顺序:自上而下、从左到右。
- break关键字结束select。
4.waitgroup和context
waitgroup和context都可以用控制并发,waitgroup类似linux中的waitpid,使用如下:
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
time.Sleep(2*time.Second)
fmt.Println("1号完成")
wg.Done()
}()
go func() {
time.Sleep(2*time.Second)
fmt.Println("2号完成")
wg.Done()
}()
wg.Wait()
fmt.Println("好了,大家都干完了,放工")
}
context
- context可以用来跟踪goroutine,比如有一个网络请求Request,每个Request都需要开启一个goroutine做一些事情,这些goroutine又可能会开启其他的goroutine。这样的话, 我们就可以通过Context,来跟踪这些goroutine,并且通过Context来控制他们的目的,这就是Go语言为我们提供的Context,中文可以称之为“上下文”。
- 另外一个实际例子是,在Go服务器程序中,每个请求都会有一个goroutine去处理。然而,处理程序往往还需要创建额外的goroutine去访问后端资源,比如数据库、RPC服务等。由于这些goroutine都是在处理同一个请求,所以它们往往需要访问一些共享的资源,比如用户身份信息、认证token、请求截止时间等。而且如果请求超时或者被取消后,所有的goroutine都应该马上退出并且释放相关的资源。这种情况也需要用Context来为我们取消掉所有goroutine
5.生产者消费者模型实现
用channel实现生产者消费者模型
func produce(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
fmt.Println("Send:", i)
}
}
func consumer(ch <-chan int) {
for i := 0; i < 10; i++ {
v := <-ch
fmt.Println("Receive:", v)
}
}
ch = make(chan int) //无缓冲
ch = make(chan int) //有缓冲
用互斥锁和条件变量实现的生产者和消费者模型:
lock := new(sync.Mutex)
cond := sync.NewCond(lock)
var count int
func producer() {
for i:=0;i<10;i++ {
lock.Lock()
count++
cond.Signal()
lock.Unlock()
}
}
func consumer(){
for i:=0;i<10;i++{
lock.Lock()
while(count == 0){
cond.Wait()
}
count--
lock.Unlock()
}
}
6.new和make的区别
new 的作用是初始化一个指向类型的指针(T),使用new函数来分配空间。传递给new 函数的是一个类型,不是一个值。返回值是 指向这个新分配的零值的指针。
make 的作用是为 slice,map 或 chan 初始化并返回引用(T)。
make(T, args)函数的目的与new(T)不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T)的一个初始化的(不是零值)的实例。
7.go触发异常的场景
空指针解析,数组越界访问,除数为0,调用panic函数 。
8.异常处理机制panic defer recover
panicing:F函数出现panic后,终止当前函数,并调用其defer函数,然后在终止调用F函数的函数并调用其defer函数,直至goroutine退出。
panic的处理:在defer函数中调用recover获得错误值,再处理错误。
9.函数变长参数使用
10.类型转换
type类型不能直接转换,只能强制转换比如:
type Myint int
var a int
var b Myint
a = b //错误
a = int(b) //正确
10.append,delete用法
delete用来删除map中的元素
11.接口方法集调用规则
- 类型 *T 的可调用方法集包含接受者为 *T 或 T 的所有方法集
- 类型 T 的可调用方法集包含接受者为 T 的所有方法
- 类型 T 的可调用方法集不包含接受者为 *T 的方法
12.函数内变量在函数结束后并不释放,有垃圾回收器释放
13.协程池实现
import "sync"
type Woker interface {
Work()
}
type Pool struct {
work chan Worker
wg sync.WaitGroup
}
func NewPool(maxRoutine int) *Pool {
p := Pool{
work: make(chan Worker)
}
p.wg.Add(maxRoutine)
for i:=0;i<maxRoutine;++i {
go func(){
for v := range work {
v.Work()
}
p.wg.Done()
}()
}
return &p
}
//提交工作到池子
func (p *Pool) Run(w Worker) {
p.work <- w
}
//关闭池子
func (p *Pool) Close() {
close(p.work)
p.wg.Wait()
}
14.go类似多态实现
package main
import "fmt"
type AnimalIF interface {
Sleep()
Age() int
Type() string
}
type Animal struct {
MaxAge int
}
/*=======================================================*/
type Cat struct {
Animal Animal
}
func (this *Cat) Sleep() {
fmt.Println("Cat need sleep")
}
func (this *Cat) Age() int {
return this.Animal.MaxAge
}
func (this *Cat) Type() string {
return "Cat"
}
/*=======================================================*/
type Dog struct {
Animal Animal
}
func (this *Dog) Sleep() {
fmt.Println("Dog need sleep")
}
func (this *Dog) Age() int {
return this.Animal.MaxAge
}
func (this *Dog) Type() string {
return "Dog"
}
/*=======================================================*/
func Factory(name string) AnimalIF {
switch name {
case "dog":
return &Dog{Animal{MaxAge: 20}}
case "cat":
return &Cat{Animal{MaxAge: 10}}
default:
panic("No such animal")
}
}
/*======================================================*/
func main() {
animal := Factory("dog")
animal.Sleep()
fmt.Printf("%s max age is: %d", animal.Type(), animal.Age())
}
15.panic和err的区别
处理错误并且在函数发生错误的地方给用户返回错误信息:照这样处理就算真的出了问题,你的程序也能继续运行并且通知给用户。panic and recover 是用来处理真正的异常(无法预测的错误)而不是普通的错误。
当发生像数组下标越界或类型断言失败这样的运行错误时,Go 运行时会触发运行时 panic,伴随着程序的崩溃抛出一个 runtime.Error 接口类型的值。这个错误值有个 RuntimeError() 方法用于区别普通错误。
15.go语言实现单例模式
双重检验加锁
type Singleton struct{
}
var ins *Singleton
var mutex sync.Mutex
func GetIns() *Singleton{
if ins == nil {
mutex.Lock()
if ins == nil {
ins = &Singleton{}
}
mutex.Unlock()
}
return ins
}
sync.Once 实现
type Singleton struct{}
var ins *Singleton
var once sync.Once
func GetIns *Singleton {
once.Do(func() {
ins = &Singleton{}
})
return ins
}
16.go实现简单工厂
package main
import (
"fmt"
)
type Op interface {
getName() string
}
type A struct {
}
type B struct {
}
type Factory struct {
}
func (a *A) getName() string {
return "A"
}
func (b *B) getName() string {
return "B"
}
func (f *Factory) create(name string) Op {
switch name {
case `a`:
return new(A)
case `b`:
return new(B)
default:
panic(`name not exists`)
}
return nil
}