AzkaBan  

平衡二叉树: 父节点的左子树和右子树的高度之差不能大于1,也就是说不能高过1层,否则该树就失衡了,
此时就要旋转节点,在编码时,我们可以记录当前节点的高度,比如空节点是-1,叶子节点是0,
非叶子节点的height往根节点递增,比如在下图中我们认为树的高度为h=2。

 

 

 

/* 1 写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出 */

import (
"runtime"
"fmt"
"time"
)


func main() {

var count int

ticket := time.NewTicker(time.Second)


for{
select {
case <-ticket.C:
ProtectRun(proc)
count++
}
}


//fmt.Println("t")
}

func ProtectRun(entry func()) {
// 延迟处理的函数
defer func() {
// 发生宕机时,获取panic传递的上下文并打印
err := recover()
switch err.(type) {
case runtime.Error: // 运行时错误
fmt.Println("runtime error:", err)
default: // 非运行时错误
fmt.Println("error:", err)
}
}()

entry()
}

func proc() {
panic("ok")
}

 

 


/* 2.使用After 实现等待组的timeout */

import (
"fmt"
"sync"
"time"
)

func main() {
wg := sync.WaitGroup{}
c := make(chan struct{})
for i := 0; i < 10; i++ {
wg.Add(1)
go func(num int, close <-chan struct{}) {
defer wg.Done()
<-close
fmt.Println(num)
}(i, c)
}

if WaitTimeout(&wg, time.Second*5) { // wg.Wait() 放在循环外, 只是起到检查任务都跑完; wg.Wait() 放在循环内, 相当于加了一个锁, 只能一个任务一个任务的执行;
close(c)
fmt.Println("timeout exit")
}
time.Sleep(time.Second * 10)
}


func WaitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {

// 要求手写代码
// 要求sync.WaitGroup支持timeout功能
// 如果timeout到了超时时间返回true
// 如果WaitGroup自然结束返回false


ch := make(chan bool)

go func(ch chan bool) {
wg.Wait()
ch <- false // 如果执行结束 则返回true
}(ch)

select {
case done := <-ch:
return done
case <-time.After(timeout):
return true
}

}

 

3. go 造成的资源丢失的原因

type query func(string) string

func exec(name string, vs ...query) string {
ch := make(chan string)
fn := func(i int) {
ch <- vs[i](name)
}
for i, _ := range vs {
go fn(i)
}
return <-ch
}

func main() {
ret := exec("111", func(n string) string {
return n + "func1"
}, func(n string) string {
return n + "func2"
}, func(n string) string {
return n + "func3"
}, func(n string) string {
return n + "func4"
})
fmt.Println(ret)
}

 

go 之后要使用 for{ select{} } 复用

func exec(name string, vs ...query) string {
ch := make(chan string)
fn := func(i int) {
ch <- vs[i](name)
}
for i, _ := range vs {
go fn(i)
}

buffer := []string{}

OuterLoop:
for{
select {
case re := <-ch:
fmt.Println("到这了?")
fmt.Println(re)
buffer = append(buffer,re)
case <- time.After(time.Second*5):
break OuterLoop
}

}

return strings.Join(buffer,",")
}

 

 

4. 枚举类
const (
name = "menglu" //0
c = iota //1
d = iota
)

fmt.Println(c) //1
fmt.Println(d) //2

 

5. 下面代码写法有什么问题?

type Stduent struct {
Age int
}
func main() {
kv := map[string]Stduent{"menglu": {Age: 21}}
kv["menglu"].Age = 22
s := []Stduent{{Age: 21}}
s[0].Age = 22
fmt.Println(kv, s)
}


改为:
func main() {
kv := map[string]*Stduent{"menglu": &Stduent{Age: 21}}
kv["menglu"].Age = 22
s := []Stduent{{Age: 21}}
s[0].Age = 22
fmt.Println(*kv["menglu"], s)
}

 


6. 补漏
补漏

func add(args ...int) int {}
add([]int{1, 3, 7}...)


func (s*Slice)Remove(value interface{}) error {

for i, v:= range *s {

if isEqual(value, v) {

*s =append((*s)[:i],(*s)[i + 1:]...)

return nil

}

}

returnERR_ELEM_NT_EXIST

}


A. 当一个goroutine获得了Mutex后,其他goroutine就只能乖乖的等待,除非该goroutine释放这个Mutex
B. RWMutex在读锁占用的情况下,会阻止写,但不阻止读
C. RWMutex在写锁占用情况下,会阻止任何其他goroutine(无论读和写)进来,整个锁相当于由该goroutine独占

 

A. 基本思路是将引用的外部包的源代码放在当前工程的vendor目录下面
B. 编译go代码会优先从vendor目录先寻找依赖包
D. 有了vendor目录后,打包当前的工程代码到其他机器的$GOPATH/src下都可以通过编译

 

91. 【中级】关于slice或map操作,下面正确的是()
A.

92. var s []int

s =append(s,1)

B.

var mmap[string]int

m["one"]= 1

C.

var s[]int

s =make([]int, 0)

s =append(s,1)

D.

var mmap[string]int

m =make(map[string]int)

m["one"]= 1

参考答案:ACD

 


D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的


A. select机制用来处理异步IO问题
B. select机制最大的一条限制就是每个case语句里必须是一个IO操作
C. golang在语言级别支持select关键字

 


33. func main() {

34. x := []string{"a", "b","c"}

35. for v := range x {

36. fmt.Print(v)

37. }

}

参考答案:012

 


panic 需要等defer 结束后才会向上传递。

 


2 以下代码有什么问题,说明原因。

type student struct {
Name string
Age int
}
func pase_student() {
m := make(map[string]*student)
stus := []student{
{Name: "zhou",Age: 24},
{Name: "li",Age: 23},
{Name: "wang",Age: 22},
} for _,stu := range stus {
m[stu.Name] =&stu //问题在这里
}
}

不能用range遍历指针, 只会得到指针指向最后一个索引的副本。

// 正确
for i:=0;i<len(stus);i++ {
m[stus[i].Name] = &stus[i]
}
for k,v:=range m{
println(k,"=>",v.Name)
}

 


select 中只要有一个case能return,则立刻执行。
当如果同一时间有多个case均能return则伪随机方式抽取任意一个执行。
如果没有一个case能return则可以执行”default”块。


func calc(indexstring, a, bint) int {
ret := a+ b
fmt.Println(index,a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a,calc("10", a, b)) a = 0
defer calc("2", a,calc("20", a, b)) b = 1
}


index:1肯定是最后执行的,但是index:1的第三个参数是一个函数,所以最先被调用

 

func main() {

23. strs := []string{"one","two", "three"}

24.

25. for _, s := range strs {

26. go func() {

27. time.Sleep(1 * time.Second)

28. fmt.Printf("%s ", s)

29. }()

30. }

31. time.Sleep(3 * time.Second)

}

参考答案:three threethree


遍历数组是正常的,问题在于 go func(s2 string) 匿名函数需要传参。

 

type Slice []int

62. func NewSlice() Slice {

63. return make(Slice, 0)

64. }

65. func (s* Slice) Add(elem int) *Slice {

66. *s = append(*s, elem)

67. fmt.Print(elem)

68. return s

69. }

70. func main() {

71. s := NewSlice()

72. defer s.Add(1).Add(2)

73. s.Add(3)

}

参考答案:132

2 作为defer第一个读取的函数,被推到栈底,然后正常执行 1 3

 

 

8 下面的代码有什么问题?

type UserAges struct {
ages map[string]int
sync.Mutex
}
func(ua*UserAges)Add(name string, age int) {
ua.Lock()
deferua.Unlock()
ua.ages[name] = age
}
func(ua*UserAges)Get(name string)int {
ifage, ok := ua.ages[name]; ok {
return age
}
return-1
}


读写要同时有锁

func (ua *UserAges)Get(namestring)int {
ua.Lock()
deferua.Unlock()
ifage, ok := ua.ages[name]; ok {
return age
}
return-1
}


10 以下代码能编译过去吗?为什么?

package main
import ( "fmt")
type People interface {
Speak(string) string
}
type Stduent struct{}
func (stu*Stduent) Speak(think string)(talk string) {
if think == "bitch" {
talk = "Youare a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Stduent{}
think := "bitch"
fmt.Println(peo.Speak(think))
}

答案是可以,这就是接口的实现与使用。

 

switch .(type){} 只能用于interface{}类型


var i interface{}
i = 1
switch i.(type){
case int:
fmt.Println("int")
}

这个无法通过编译
i := 1
switch i.(type){
case int:
fmt.Println("int")
}


new 出来的是指针, make 出来的值


list := new([]int)
*list = append(*list,1)

list2 := make([]int,0)
list2 = append(list2,1)


进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。


指针为空 var a *int = nil 与 值为空 x == nil 是两码事


常量不同于变量的在运行期分配内存,常量通常会被编译器在预处理阶段直接展开,作为指令数据使用,


const cl = 100

fmt.Println(cl)
fmt.Println(&cl) 常量不分配内存地址


type M1 int //M1 为新类型, 需要强转赋值
type M2 = int //M2 为别名

var i int = 9

var i1 M1 = M1(i)
var i2 M2 = i

fmt.Println(i1,i2)

 


func test() []func() {
var funs []func()
for i := 0; i < 2; i++ {
funs = append(funs, func() { // 匿名函数就是闭包, 具有延迟执行与静态保存的特性, 要么直接往匿名函数中传参, 要么在外层定义变量保存值;
println(&i, i)
})
}
return funs
}


funs := test()
for _, f :=range funs{
f()
}


func main() {

funs := test()
for _, f :=range funs{
f()
}
}

 

x := i

funs = append(funs, func() {
println(&x, x)
})

 

###################################################################
grpc:

//向grpc服务发送信号, 检测是否连接;
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
svc.Close()
log.Info("zeus-service exit")
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}

http service 之间通过 header 形式进行传递,rpc service 之间通过 rpc metadata 形式进行传递。

1. 先初始化 serve 数据库连接池等
... 创建内部rpc服务,
net/rpc/warden
func New(c *conf.Config, s *v1srv.ZeusService) *warden.Server {
ws := warden.NewServer(nil)
v1pb.RegisterZeusServer(ws.Server(), s)
ws, err := ws.Start()
if err != nil {
panic(err)
}
return ws
}

如果我们用socket实现RPC,那么就能获取性能上的优势。在大并发的情况下,RPC的性能优于RESTful
所以通常情况下,我们对内是用rpc提供服务,对外用RESTful提供服务

golang官方给我们提供了一个net/rpc的库使用,它支持tpc,http,JSONRPC数据传输。但是它只能用于go内部使用,为什么?
因为它使用了一个go自带的库(encoding/gob)来编码和解码。如果需要跨平台那么需要其它的rpc框架了,比如thrift,gRPC等等


net/rpc的远程调用还有一些附加条件:

1.函数必须是导出的(首字母大写)
2.必须有两个导出类型的参数
3.第一个参数是接收的参数,第二个参数是返回给客- 户端的参数,第二个参数必须是指针类型的
4.函数还要有一个返回值error


type Arith int

func (t *Arith) Multiply(args *string, reply *int) error {
return nil
}

arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1212")
if e != nil {
log.Fatal("listen error: ", e)
}
http.Serve(l, nil)

简单分析一下上面的例子,先实例化了一个Arith对象arith,然后给arith注册了rpc服务,然后把rpc挂载到http服务上面,
当http服务打开的时候我们就可以通过rpc客户端来调用arith中符合rpc标准的的方法了

// 用到3个方法, rpc, net, http


//客户端
client, err := rpc.DialHTTP("tcp", "127.0.0.1:1212")
client.Call("Arith.Multiply", "call", &reply)

 

如何在go routing中捕获异常, 并恢复:
一种方法是抽象出方法,
ProtectedRun(f func()){
defer func(){
err := recover()
}
f()
}

go ProtectRun()


另一种方法是, 使用反射切入

func newF(){

go func(){
defer func(){
err := recover()

ff := reflect.ValueOf(f)
ff.Call()
}
}

}

 


#############################################################################################
嵌套服务goroutine

当一个服务包含多个子服务时, 每个子服务又都有goroutine, 那如何只关闭这个子服务的goroutine?

func main(){

ctx, cancel := context.WithCancel(context.Backgroud())
go doStuff(ctx)

time.Sleep(10*time.Seconds)
cancel() //关闭该子服务, 将关闭信息传入context通道;

}

 

func doStuff(ctx context.Backgroud){
for{
select{
case <-ctx.Done():
return
default:
fmt.Print("working")
}
}
}

 

// 两种单例模式


import "sync"
import "sync/atomic"

var initialized uint32
...

func GetInstance() *singleton {

if atomic.LoadUInt32(&initialized) == 1 {
return instance
}

mu.Lock()
defer mu.Unlock()

if initialized == 0 {
instance = &singleton{}
atomic.StoreUint32(&initialized, 1)
}

return instance
}

 

import (
"sync"
)

type singleton struct {
}

var instance *singleton
var once sync.Once

func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}

 

posted on 2019-09-04 14:18  AzkaBan  阅读(476)  评论(0编辑  收藏  举报