平衡二叉树: 父节点的左子树和右子树的高度之差不能大于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
}